一、Nonheap介绍
Java虚拟机将内存划分为两个区域:heap和nonheap。
Heap区用于存储Java对象和JVM需要管理的数据。
Nonheap区用于存储JVM本身和Java类相关的信息。
除了Static数据和代码区,其他的Java代码都在Heap中运行。
而非Java数据的JVM或者Java类定义等信息是存储在Nonheap中的。
二、Nonheap的组成
Java中的Nonheap主要由Java虚拟机自己管理。
主要包含以下几个主要的元素:
1. Code Cache
Code Cache存放被JIT编译后的本地代码(native code)。
2. PermGen Space
PermGen Space存储Java类信息(例如类名称、访问修饰符等),以及方法和字段的元数据信息。
3. Metaspace
Java SE 8中,PermGen space被Metaspace替换,因为PermGen space在过多使用时会导致内存问题。所以,Metaspace用来存放类元信息。
4. Stack
Stack中存储着线程的运行状态,包括方法调用堆栈、局部变量信息和操作数栈。
5. Direct Buffers
Direct Buffers是non-heap的一部分。
6. JNI
Java Native Interface,是Java中调用非Java的C或其他本地库的接口。
三、代码示例
class NonHeapDemo{ public static void main(String[] args) { //查找NonHeap区Header容量 long nonheapHeaderSize = sun.misc.VM.maxDirectMemory() - sun.misc.VM.currentDirectMemory(); System.out.println("NonHeap Header Size: " + nonheapHeaderSize); //获取Code Cache大小 long codeCacheSize = ((com.sun.management.HotSpotDiagnosticMXBean) ManagementFactory.getDiagnosticMXBean()).getLargestCompilationLevel(); System.out.println("Code Cache Size: " + codeCacheSize); //获取PermGen Space或者Metaspace的大小 long metaspaceSize = ((com.sun.management.GarbageCollectorMXBean) ManagementFactory.getGarbageCollectorMXBeans().get(0)).getMemoryPoolNames().stream() .filter(pool -> pool.contains("Metaspace")) .map(ManagementFactory::getMemoryPoolMXBean) .mapToLong(MemoryPoolMXBean::getUsage).mapToLong(Usage::getMax).sum(); System.out.println("PermGen Space/Metaspace Size: " + metaspaceSize); //获取Direct Buffers大小 long directBufferSize = ((com.sun.management.BufferPoolMXBean) ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class).get(0)).getTotalCapacity(); System.out.println("Direct Buffer Size: " + directBufferSize); //获取JNI信息 System.out.println("JNI: " + sun.misc.SharedSecrets.getJavaLangAccess().getJNIMethods().size()); } }
四、Nonheap的重要性
Nonheap对Java应用的运行也有很大的影响。
例如,Nonheap中的Direct Buffers如果不管控得当,会导致应用内存泄漏,最终导致JVM崩溃。
而且,PermGen Space也需要注意大小,因为如果PermGen Space不足,可能会导致“内部错误:Java堆空间溢出”,这是因为PermGen的比例比例比例如今应用的内存。
因此,掌握Nonheap的基本知识和调优方法是十分重要的。