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