【深刻Java編程】JVM源碼分析之堆外內存徹底解讀

概述

廣義的堆外內存

說到堆外內存,那你們確定想到堆內內存,這也是咱們你們接觸最多的,咱們在jvm參數裏一般設置-Xmx來指定咱們的堆的最大值,不過這還不是咱們理解的Java堆,-Xmx的值是新生代和老生代的和的最大值,咱們在jvm參數裏一般還會加一個參數-XX:MaxPermSize來指定持久代的最大值,那麼咱們認識的Java堆的最大值實際上是-Xmx和-XX:MaxPermSize的總和,在分代算法下,新生代,老生代和持久代是連續的虛擬地址,由於它們是一塊兒分配的,那麼剩下的均可以認爲是堆外內存(廣義的)了,這些包括了jvm自己在運行過程當中分配的內存,codecache,jni裏分配的內存,DirectByteBuffer分配的內存等等java

狹義的堆外內存

而做爲java開發者,咱們常說的堆外內存溢出了,實際上是狹義的堆外內存,這個主要是指java.nio.DirectByteBuffer在建立的時候分配內存,咱們這篇文章裏也主要是講狹義的堆外內存,由於它和咱們平時碰到的問題比較密切算法

JDK/JVM裏DirectByteBuffer的實現

DirectByteBuffer一般用在通訊過程當中作緩衝池,在mina,netty等nio框架中家常便飯,先來看看JDK裏的實現:框架

DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;



    }

經過上面的構造函數咱們知道,真正的內存分配是使用的Bits.reserveMemory方法jvm

static void reserveMemory(long size, int cap) {
        synchronized (Bits.class) {
            if (!memoryLimitSet && VM.isBooted()) {
                maxMemory = VM.maxDirectMemory();
                memoryLimitSet = true;
            }
            // -XX:MaxDirectMemorySize limits the total capacity rather than the
            // actual memory usage, which will differ when buffers are page
            // aligned.
            if (cap <= maxMemory - totalCapacity) {
                reservedMemory += size;
                totalCapacity += cap;
                count++;
                return;
            }
        }

        System.gc();
        try {
            Thread.sleep(100);
        } catch (InterruptedException x) {
            // Restore interrupt status
            Thread.currentThread().interrupt();
        }
        synchronized (Bits.class) {
            if (totalCapacity + cap > maxMemory)
                throw new OutOfMemoryError("Direct buffer memory");
            reservedMemory += size;
            totalCapacity += cap;
            count++;
        }

    }

經過上面的代碼咱們知道能夠經過-XX:MaxDirectMemorySize來指定最大的堆外內存,那麼咱們首先引入兩個問題ide

  • 堆外內存默認是多大
  • 爲何要主動調用System.gc()

堆外內存默認是多大

若是咱們沒有經過-XX:MaxDirectMemorySize來指定最大的堆外內存,那麼默認的最大堆外內存是多少呢,咱們仍是經過代碼來分析函數

上面的代碼裏咱們看到調用了sun.misc.VM.maxDirectMemory()this

private static long directMemory = 64 * 1024 * 1024;

    // Returns the maximum amount of allocatable direct buffer memory.
    // The directMemory variable is initialized during system initialization
    // in the saveAndRemoveProperties method.
    //
    public static long maxDirectMemory() {
        return directMemory;
    }

看到上面的代碼以後是否是誤覺得默認的最大值是64M?其實不是的,說到這個值得從java.lang.System這個類的初始化提及spa

/**
     * Initialize the system class.  Called after thread initialization.
     */
    private static void initializeSystemClass() {

        // VM might invoke JNU_NewStringPlatform() to set those encoding
        // sensitive properties (user.home, user.name, boot.class.path, etc.)
        // during "props" initialization, in which it may need access, via
        // System.getProperty(), to the related system encoding property that
        // have been initialized (put into "props") at early stage of the
        // initialization. So make sure the "props" is available at the
        // very beginning of the initialization and all system properties to
        // be put into it directly.
        props = new Properties();
        initProperties(props);  // initialized by the VM

        // There are certain system configurations that may be controlled by
        // VM options such as the maximum amount of direct memory and
        // Integer cache size used to support the object identity semantics
        // of autoboxing.  Typically, the library will obtain these values
        // from the properties set by the VM.  If the properties are for
        // internal implementation use only, these properties should be
        // removed from the system properties.
        //
        // See java.lang.Integer.IntegerCache and the
        // sun.misc.VM.saveAndRemoveProperties method for example.
        //
        // Save a private copy of the system properties object that
        // can only be accessed by the internal implementation.  Remove
        // certain system properties that are not intended for public access.
        sun.misc.VM.saveAndRemoveProperties(props);

         ......

        sun.misc.VM.booted();
    }

上面這個方法在jvm啓動的時候對System這個類作初始化的時候執行的,所以執行時間很是早,咱們看到裏面調用了sun.misc.VM.saveAndRemoveProperties(props):netty

閱讀全文直接點擊:http://click.aliyun.com/m/9972/code

相關文章
相關標籤/搜索