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();
    }
}
JVM与直接内存分析JVM与直接内存分析JVM与直接内存分析 可以发现直接内存 heap空间与meta空间都是没有影响的,但是对应进程占用的内存是有直接影响的。 JVM与直接内存分析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();
    }
}
JVM与直接内存分析JVM与直接内存分析 可以发现当创建对象的时候,发生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 1048576K
4.直接内存+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();
    }
}
JVM与直接内存分析JVM与直接内存分析JVM与直接内存分析 可以看到java进程消耗的内存是3.6g已经超过了Xmx配置的大小,不过gc之后一样会被回收。

总结

1、直接内存申请的总和大小不能超过-Xmx配置的大小 2、直接内存申请的总和大小不能超过-XX:MaxDirectMemorySize配置的大小 3、直接内存申请的总和不能超过系统允许的最大值 4、gc可以将直接内存进行回收 5、直接内存虽然要小于-Xmx配置的大小,但是直接内存+heap+metaspace会大于-Xmx配置的大小
上一篇:【计理01组02号】程序流程之分支


下一篇:MAC 设置环境变量path的常用方法