学习jvm就得了解jvm的内存区域,我对内存区域做了一个简单的梳理
方法区:[线程共有]
用于存放被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等
堆(heap):[线程共有]
新生代YoungGeneration
- Eden
- Survivor(From)
- Survivor(To)
默认比例:8:1:1
GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。然后紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
老年代OldGeneration
老年代主要存放应用中生命周期长的内存对象。老年代比较稳定,不会频繁的进行MajorGC。但是在MaiorGC之前才会先进行一次MinorGc,使得新生的对象进入老年代而导致空间不够才会触发。当无法找到足够大的连续空间分配给新创建的较大对象也会提前触发一次MajorGC进行垃圾回收腾出空间。
在老年代中,MajorGC采用了标记清除算法:首先扫描一次所有老年代里的对象,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长。因为要扫描再回收。MajorGC会产生内存碎片,当老年代也没有内存分配给新来的对象的时候,就会抛出OOM(Out of Memory)异常。
永久代(jdk1.8以后被元空间代替)
不属于堆内存,是方法区的一种实现,各大厂商对方法区有各自的实现。
主要存放Class和Meta(元数据)的信息。Classic在被加载的时候被放入永久区域,它和存放的实例的区域不同。在Java8中,永久代已经被移除,取而代之的是一个称之为“元数据区”(元空间)的区域。元空间和永久代类似,都是对JVM中规范中方法的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存的限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中。这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制
程序计数器:
当前线程执行字节码的行号指示器
虚拟机栈(JVM执行Java方法):[线程私有]
java方法执行的内存模型,每个方法执行都会创建一个栈帧(局部变量表、操作数栈、动态链接、方法出口),每个方法从开始调用到结束都对应一个栈帧在虚拟机栈中入栈到出栈的过程
本地方法栈(JVM执行本地方法):[线程私有]
HotSpot把本地方法栈和虚拟机栈合二为一
直接内存(Direct Memory):
jdk1.4的NIO,可以使用native函数直接分配堆外内存,受本机总内存和处理器寻址空间的限制