zoukankan      html  css  js  c++  java
  • 原生内存(堆外内存)

    在JVM使用的内存中,通常堆消耗的部分最多,但是JVM也会为内部操作分配一些内部。这些非堆内存就是原生内存。

    应用中可以通过JNI的malloc()类似的方法或者是使用NIO的API分配。JVM使用的原生内存和堆内存的总量,就是一个应用总的内存占用(Footprint)。

    1、测量内存占用

    unix系统中,像top和ps这样的程序可以给出基本数据;

    window系统中,可以使用perfmon或VMMap;

    何种平台,都需要看看进程实际分配的内存(这与保留的内存完全不同)。分配内存(也叫提交内存),保留内存(有时也叫作虚拟内存)。

    分配内存和保留内存区别:

    通过示例说明,JVM必须告知操作系统,它的堆可能需要多达2GB的内存,所以会保留这么多内存:操作系统承诺,当JVM因为要增加堆而尝试分配额外的内存时,这些内存时可以获取到的。最初分配的内存时512MB,而且这就是实际用到的全部内存。也即是提交内存。提交内存的量随堆的重新调整而波动;特别是,提交内存会随着堆的增加而相应增加。

    线程栈是个例外。JVM每次创建线程时,操作系统分配一些原生内存来保存线程栈,向线程提交更多内存(至少要等到线程退出)。线程栈是在创建时全部分配的。

    2、原生NIO缓冲区

    开发者 可以通过JNI调用来分配原生内存,但是如果NIO字节缓冲区是通过allocateDirect()方法创建的,则也会分配原生内存。从性能角度看,原生字节缓冲区非常重要,因为它们支持原生代码和Java代码在不复制的情况下共享数据。

     调用allocateDirect()方法非常昂贵,所以应该尽可能重用直接字节缓冲区。理想的情况是,线程是独立的,而且每个线程持有一个直接字节缓存区作为线程局部变量。直接字节缓冲区的对象池可能更有用。

    字节缓存区也可以切割管理。应用可以分配一个非常大的直接字节缓冲区,然后每个请求使用ByteBuffer类的slice()方法从中分配一部分。如果每次不能分配相同大小,这种方案也很难处理:会形成碎片。而且不像堆那样还可以对碎片进行压缩,字节缓冲区中的不同片段是无法压缩的,所以只有当所有片段大小都相同时,这种解决方案才好用。

    直接字节缓冲区所分配的内存总量:可以通过设置-XX:MaxDirectMemorySize=N标志来指定。从JAVA7开始,这个标志默认值为0,以为没有限制(当然受制于地址空间大小,以及操作系统对进程的各种限制)。

    3、原生内存跟踪

    从java8开始,借助-XX:NativeMemoryTracking=off|summary|detail这个选项,JVM支持我们一窥它是如何分配原生内存的。原生内存跟踪(Native Memory Tracking,NMT)默认是关闭的(off)。如果开启了概要模式(summary)或详情模式(detail),可以随时通过jcmd命令获得原生内存的信息:

    jcmd process_id VM.native_memory summary

    如果JVM是使用-XX:+PrintNMTStatistics参数(默认false)启动的,它会在程序退出时打印原生内存分配信息。

    示例:

    package com.dxz.jvm;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 1堆溢出信息
     * @VM args:-verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails
     * 
     * 2原生内存信息打印
     * @VM args:-verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions  -XX:+PrintNMTStatistics -XX:NativeMemoryTracking=summary
     */
    public class HeapOutOfMemory {
    
        public static void main(String[] args) {
            List<TestObject> cases = new ArrayList<TestObject>();
            HeapOutOfMemory heapOutOfMemory = new HeapOutOfMemory();
            int i = 0;
            while (i <10) {
                cases.add(heapOutOfMemory.new TestObject());
                i++;
            }
            try {
                TimeUnit.SECONDS.sleep(60);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        public class TestObject {
            private double a = 34.53;
            private Integer b = 9999999;
        }
    }

    用jcmd

    D:\workspace\study\target>jcmd 11532 VM.native_memory summary
    11532:
    
    Native Memory Tracking:
    
    Total: reserved=1361449KB, committed=62425KB
    -                 Java Heap (reserved=20480KB, committed=20480KB)
                                (mmap: reserved=20480KB, committed=20480KB)
    
    -                     Class (reserved=1062013KB, committed=10109KB)
                                (classes #421)
                                (malloc=5245KB #154)
                                (mmap: reserved=1056768KB, committed=4864KB)
    
    -                    Thread (reserved=15423KB, committed=15423KB)
                                (thread #16)
                                (stack: reserved=15360KB, committed=15360KB)
                                (malloc=45KB #82)
                                (arena=18KB #30)
    
    -                      Code (reserved=249636KB, committed=2572KB)
                                (malloc=36KB #314)
                                (mmap: reserved=249600KB, committed=2536KB)
    
    -                        GC (reserved=6667KB, committed=6611KB)
                                (malloc=5771KB #117)
                                (mmap: reserved=896KB, committed=840KB)
    
    -                  Compiler (reserved=132KB, committed=132KB)
                                (malloc=2KB #22)
                                (arena=131KB #3)
    
    -                  Internal (reserved=5380KB, committed=5380KB)
                                (malloc=5316KB #1365)
                                (mmap: reserved=64KB, committed=64KB)
    
    -                    Symbol (reserved=1495KB, committed=1495KB)
                                (malloc=943KB #111)
                                (arena=552KB #1)
    
    -    Native Memory Tracking (reserved=38KB, committed=38KB)
                                (malloc=3KB #37)
                                (tracking overhead=35KB)
    
    -               Arena Chunk (reserved=185KB, committed=185KB)
                                (malloc=185KB)
    
    
    D:\workspace\study\target>

    说明:

    Total: reserved=1361449KB(保留内存), committed=62425KB(提交内存)
    -                 Java Heap (reserved=20480KB(20MB,同参数设置一致), committed=20480KB)
                                (mmap: reserved=20480KB, committed=20480KB)
    堆使用情况:
    Java Heap (reserved=20480KB, committed=20480KB)
                                (mmap: reserved=20480KB, committed=20480KB)
    保留内存为20MB与设置的相同。

    -                     Class (reserved=1062013KB, committed=10109KB)
                                (classes #421)
                                (malloc=5245KB #154)
                                (mmap: reserved=1056768KB, committed=4864KB)
    用于保存类的元数据的原生内存。classes #421是实际用于保存程序中的421个类而占用的内存相比,JVM保留的内存要更多。

    -                    Thread (reserved=15423KB, committed=15423KB)
                                (thread #16)
                                (stack: reserved=15360KB, committed=15360KB)
                                (malloc=45KB #82)
                                (arena=18KB #30)
    thread #16,表示16个线程,分配的总内存有15423KB,平均一个线程是1MB。

    -                      Code (reserved=249636KB, committed=2572KB)
                                (malloc=36KB #314)
                                (mmap: reserved=249600KB, committed=2536KB)
    JIT的代码缓存:根据上面的类数量来的,所以内存占用不是很多。

    -                        GC (reserved=6667KB, committed=6611KB)
                                (malloc=5771KB #117)
                                (mmap: reserved=896KB, committed=840KB)
    GC算法的处理锁使用的一些堆外空间。


    -                  Compiler (reserved=132KB, committed=132KB)
                                (malloc=2KB #22)
                                (arena=131KB #3)
    这个区域是供编译器自身操作使用的,这与生成的代码放在代码缓存中是不同的。

    -                    Symbol (reserved=1495KB, committed=1495KB)
                                (malloc=943KB #111)
                                (arena=552KB #1)

    保留字符串(Interned String)的引用与符号表引用放在这里。

    -    Native Memory Tracking (reserved=38KB, committed=38KB)
                                (malloc=3KB #37)
                                (tracking overhead=35KB)
    NMT本身的操作也需要一些空间。


    NMT跟踪

    NMT也支持跟踪内存分配随时间的变化情况。如果JVM在启动时启用了NMT,可以使用如下命令确定内存的基线使用情况:

    jcmd process_id VM.native_memory baseline

    4、针对不同操作系统优化JVM

    JVM可以利用一些调优选项来优化操作系统内存的使用。

    4.1、大页

    java支持-XX:+UseLargepages选项。其默认值跟具体的操作系统配置有关。在windows上,必须在操作系统中启动大页。(默认使用常规页)

    在linux上,UseLargepages默认不会启用,要支持大页,需要配置一下操作系统。

    在Solaris上,不要什么操作系统方面配置,默认启用大页。

    linux大页

    linux透明页

    大页大小

    5、压缩的oop
    oop:ordinary object pointer,即普通对象指针,JVM将其用作对象引用的句柄。

  • 相关阅读:
    GC算法 垃圾收集器
    Distinct
    生产者消费者实现
    单例模式(七种实现方法)
    【JUC】JDK1.8源码分析之AbstractQueuedSynchronizer
    【JUC】JDK1.8源码分析之ConcurrentHashMap
    【集合框架】JDK1.8源码分析之HashMap
    一个整数,它加上100后是一个完全平方数, 再加上168又是一个完全平方数,请问该数是多少?
    猴子吃桃问题:猴子第一天摘下若干个桃子, 当即吃了一半,还不过瘾,又多吃了一个; 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。 * 以后每天早上都吃了前一天剩下的一半零一个。 到第10天早上想再吃时,见只剩下一个桃子了。 求第一天共摘了多少。 * 1.程序分析:采取逆向思维的方法,从后往前推断。
    输出9*9口诀 输出9*9乘法表
  • 原文地址:https://www.cnblogs.com/duanxz/p/2630284.html
Copyright © 2011-2022 走看看