JVM与直接内存分析
2021/12/30 7:10:57
本文主要是介绍JVM与直接内存分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
之前在看netty的时候,不断的提到直接内存与零拷贝,所以就想看看JVM与直接内存的之间的关系;环境准备
系统:macOS 11+ Jdk:jdk1.8 内存:16G直接内存分析
1.直接内存与heap空间、meta空间之间的关系 Code-1:申请直接内存//-Xmx2g -XX:+PrintGCDetails public class BufferTest1 { private static final int BUFFER = 1024 * 1024 * 1024;//1GB public static void main(String[] args) throws InterruptedException { Thread.sleep(5000l); Scanner scanner = new Scanner(System.in); System.out.println("请求指示:"); scanner.next(); //直接分配本地内存空间 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER); System.out.println("直接内存分配完毕,请求指示!"); scanner.next(); System.out.println("直接内存开始释放!"); byteBuffer = null; System.gc(); scanner.next(); } }可以发现直接内存 heap空间与meta空间都是没有影响的,但是对应进程占用的内存是有直接影响的。 2.直接内存最大内存 Code-2:多申请几个直接内存
//-Xmx2g -XX:+PrintGCDetails public class BufferTest2 { private static final int BUFFER = 1024 * 1024 * 1024;//1GB private static final int BUFFER2 = 512 * 1024 * 1024; public static void main(String[] args) throws InterruptedException { Thread.sleep(5000l); Scanner scanner = new Scanner(System.in); System.out.println("请求指示:"); scanner.next(); //直接分配本地内存空间1 ByteBuffer byteBuffer1 = ByteBuffer.allocateDirect(BUFFER); System.out.println("直接内存1分配完毕,请求指示!"); scanner.next(); ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(BUFFER2); System.out.println("直接内存2分配完毕,请求指示!"); scanner.next(); ByteBuffer byteBuffer3 = ByteBuffer.allocateDirect(BUFFER2); System.out.println("直接内存3分配完毕,请求指示!"); scanner.next(); System.out.println("直接内存开始释放!"); byteBuffer1 = null; byteBuffer2 = null; byteBuffer3 = null; System.gc(); scanner.next(); } }
请求指示: 1 直接内存1分配完毕,请求指示! 2 直接内存2分配完毕,请求指示! 3 [GC (System.gc()) [PSYoungGen: 10486K->1966K(76288K)] 10486K->1974K(251392K), 0.0027432 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] [Full GC (System.gc()) [PSYoungGen: 1966K->0K(76288K)] [ParOldGen: 8K->1801K(175104K)] 1974K->1801K(251392K), [Metaspace: 4178K->4178K(1056768K)], 0.0069622 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:695) at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311) at com.kaikela.demo2.home.BufferTest2.main(BufferTest2.java:42) Heap PSYoungGen total 76288K, used 1748K [0x0000000795580000, 0x000000079aa80000, 0x00000007c0000000) eden space 65536K, 2% used [0x0000000795580000,0x0000000795735018,0x0000000799580000) from space 10752K, 0% used [0x0000000799580000,0x0000000799580000,0x000000079a000000) to space 10752K, 0% used [0x000000079a000000,0x000000079a000000,0x000000079aa80000) ParOldGen total 175104K, used 1801K [0x0000000740000000, 0x000000074ab00000, 0x0000000795580000) object space 175104K, 1% used [0x0000000740000000,0x00000007401c2490,0x000000074ab00000) Metaspace used 4218K, capacity 4674K, committed 4864K, reserved 1056768K class space used 466K, capacity 494K, committed 512K, reserved 1048576K Process finished with exit code 1可以看出,直接内存大小不能超过jvm的最大限制(-Xmx),当然也可以限制直接内存的大小(-XX:MaxDirectMemorySize)。比如设置-XX:MaxDirectMemorySize=1g,那么上面的代码在执行第二次空间申请的时候就会报OOM。 3.直接内存与GC 上面的例子可以发现,我们在主动进行GC的时候,那我们可以设想一个场景,我们申请了一个很大直接内存,然后开始大量的创建对象,那么整个JVM内存的表现是怎么样的 Code-3:直接内存与GC
// -Xmx2g -XX:+PrintGCDetails public class BufferTest { private static final int BUFFER = 1700 * 1024 * 1024;//1.6GB public static void main(String[] args) throws InterruptedException { Scanner scanner = new Scanner(System.in); System.out.println("请求指示:"); scanner.next(); //直接分配本地内存空间 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER); System.out.println("直接内存分配完毕,请求指示!"); scanner.next(); List<TestObj> bigObj = new ArrayList<>(); for (int n = 0; n < 5000000; n++) { bigObj.add(new TestObj(n)); } System.out.println(bigObj); scanner.next(); System.out.println("直接内存开始释放!"); byteBuffer = null; System.gc(); scanner.next(); } }可以发现当创建对象的时候,发生GC的时候,申请的直接内存也会被回收。
请求指示: 1 直接内存分配完毕,请求指示! 2 [GC (Allocation Failure) [PSYoungGen: 65536K->10728K(76288K)] 65536K->34945K(251392K), 0.0276737 secs] [Times: user=0.17 sys=0.02, real=0.03 secs] [GC (Allocation Failure) [PSYoungGen: 76264K->10749K(76288K)] 100481K->92821K(251392K), 0.0606339 secs] [Times: user=0.41 sys=0.04, real=0.07 secs] [GC (Allocation Failure) [PSYoungGen: 68029K->10747K(76288K)] 150101K->142462K(251392K), 0.0662871 secs] [Times: user=0.47 sys=0.03, real=0.06 secs] [Full GC (Ergonomics) [PSYoungGen: 10747K->0K(76288K)] [ParOldGen: 131715K->141458K(285184K)] 142462K->141458K(361472K), [Metaspace: 9355K->9355K(1058816K)], 1.4972584 secs] [Times: user=8.48 sys=0.09, real=1.50 secs] [GC (Allocation Failure) [PSYoungGen: 65536K->10752K(128512K)] 206994K->203458K(413696K), 0.0623276 secs] [Times: user=0.34 sys=0.05, real=0.06 secs] 1 直接内存开始释放! [GC (System.gc()) [PSYoungGen: 101794K->10752K(128512K)] 294500K->288159K(413696K), 0.0895192 secs] [Times: user=0.55 sys=0.07, real=0.09 secs] [Full GC (System.gc()) [PSYoungGen: 10752K->0K(128512K)] [ParOldGen: 277407K->260989K(285184K)] 288159K->260989K(413696K), [Metaspace: 9526K->9526K(1058816K)], 1.1158031 secs] [Times: user=6.84 sys=0.02, real=1.12 secs] Heap PSYoungGen total 128512K, used 3498K [0x0000000795580000, 0x000000079e080000, 0x00000007c0000000) eden space 117760K, 2% used [0x0000000795580000,0x00000007958ea8a0,0x000000079c880000) from space 10752K, 0% used [0x000000079d580000,0x000000079d580000,0x000000079e000000) to space 512K, 0% used [0x000000079e000000,0x000000079e000000,0x000000079e080000) ParOldGen total 285184K, used 260989K [0x0000000740000000, 0x0000000751680000, 0x0000000795580000) object space 285184K, 91% used [0x0000000740000000,0x000000074fedf5b8,0x0000000751680000) Metaspace used 9540K, capacity 10054K, committed 10240K, reserved 1058816K class space used 1103K, capacity 1211K, committed 1280K, reserved 1048576K4.直接内存+heap+metaspace=进程内存 根据code-3的例子,我们翻过来思考,如果先将heap空间用完,在去申请直接内存是否还能够成功? Code-4
public class BufferTest3 { private static final int BUFFER = 1740 * 1024 * 1024;//1.6GB public static void main(String[] args) throws InterruptedException { Scanner scanner = new Scanner(System.in); System.out.println("请求指示:"); scanner.next(); List<TestObj> bigObj = new ArrayList<>(); for (int n = 0; n < 5000000; n++) { bigObj.add(new TestObj(n)); } System.out.println(bigObj); scanner.next(); //直接分配本地内存空间 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER); System.out.println("直接内存分配完毕,请求指示!"); scanner.next(); System.out.println("直接内存开始释放!"); byteBuffer = null; System.gc(); scanner.next(); } }可以看到java进程消耗的内存是3.6g已经超过了Xmx配置的大小,不过gc之后一样会被回收。
总结
1、直接内存申请的总和大小不能超过-Xmx配置的大小 2、直接内存申请的总和大小不能超过-XX:MaxDirectMemorySize配置的大小 3、直接内存申请的总和不能超过系统允许的最大值 4、gc可以将直接内存进行回收 5、直接内存虽然要小于-Xmx配置的大小,但是直接内存+heap+metaspace会大于-Xmx配置的大小这篇关于JVM与直接内存分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略