Java虚拟机运行时数据区
将从《深入理解Java虚拟机:JVM高级特性与最佳实践》和 The Java® Virtual Machine Specification 中对于Java运行时数据区的描述整理记录一下
从书中截取的Java运行时数据区
1、程序计数器
2、Java虚拟机栈
Java虚拟机栈是线程私有的,创建线程的时候生成。存储局部变量表、局部结果。作用于方法的调用和返回
定义了两种异常:
1、如果一个线程的请求的栈的深度超过了Java虚拟机栈的深度,抛出StackOverflowError 。
2、如果Java虚拟机栈可以动态扩展,尝试扩展的时候没有足够的内存,或者没有足够的内存为新线程分配虚拟机栈,则抛出OutOfMemoryError
-Xss设置设置每个栈的大小
3、堆
堆在虚拟机启动的时候创建,是由所有线程共享的,所有的类实例和数组都在堆上分配
定义了一种异常:
如果堆种没有足够的空间完成实例的分配,则抛出OutOfMemoryError
可以通过 -Xms设置堆的最小值 -Xmx设置最大值 eg:-Xmx512m
堆是Java垃圾回收的主战场,其中又分为Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)
1)一个对象new出来后就出现在伊甸园区无忧无虑的生活,GC会定时或者手动的来收取保护费(对象有没有引用),穷人就直接kill掉。交了保护费的对象就会进入幸存者区。
2)并不是进入幸存者区后就安全了,但至少可以活段时间。GC还是会来进行敲诈,亿万富翁每次都给钱,GC很满意,就让其进入了Genured Gen(养老区)。万元户经不住几次敲诈就没钱了,GC看没有啥价值啦,就直接kill掉了
3)进入到养老区的人基本就可以保证人身安全啦,但是亿万富豪有的也会挥霍成穷光蛋,只要钱没了,GC还是kill掉
关于此区域的回收:
IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,当回收时将Eden和Servivor中还存活的对象一次性复制到另一块Survivor空间里,然后清理刚才的将Eden和Servivor空间。HotSpot虚拟机默认的Eden和Servivor的大小比例是8:1。当Servivor控件不够用时,需要依赖其他内存(老年代)进行分配担保(Handle Promotion)
新生代中,每次垃圾收集都有大批的对象死去,只有少量存活,就使用复制算法,只需要付出少数存活对象的复制成本就可以完成收集。老年代中因为对象存活率高,没有额外空间进行分配担保,就必须使用「标记-清理」或者「标记-整理」算法来完成回收。
4、方法区
方法区在虚拟机启动的时候创建,逻辑上是堆的一部分,由所有线程共享。它存储每个类结构,运行时的常量池,字段和方法的数据,方法和构造方法。
当方法区不能满足内存分配时,则抛出OutOfMemoryError
常说的Perm Gen(永久代)就处于方法区。
可以通过-XX:PermSize设置永久带的初始值,-XX:MaxPermSize设置永久带的最大值
关于此区域的回收:
永久带垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与Java堆种回收对象很类似,判断无用类条件比较苛刻,如下
1)所有类的实例都已经被回收,Java种不存在任何类的实例
2)加载该类的ClassLoader已经被回收
3)该类对应的java.lang.Class对象没有被任何地方引用,无法通过反射访问该类的方法。
5、运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等信息的描述外,还有一段信息是常量池,用于存放编译期生成的各种字面量和符号引用,
这部分内容将在类加载后进入方法区的运行时常量池中存放
当常量池无法再申请内存时,则抛出OutOfMemoryError
6、本地方法栈
本地方法栈与虚拟机栈发挥的功能类似,只是本地方法栈为native方法服务。
1、如果一个线程的请求的栈的深度超过了Java虚拟机栈的深度,抛出StackOverflowError 。
2、如果Java虚拟机栈可以动态扩展,尝试扩展的时候没有足够的内存,或者没有足够的内存为新线程分配虚拟机栈,则抛出OutOfMemoryError
7、直接内存
直接内存(DirectMemory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现
《深入理解Java虚拟机:JVM高级特性与最佳实践》