JVM内存模型与垃圾回收
在平时开发中似乎不关注jvm底层,只是专注在业务层和框架使用方面,这样在处理某些问题的时候只能寻求百度谷歌,却不知其所以然,所以这篇博客总结jvm垃圾回收机制,说道回收机制,那跟内存是分不开的,因为jvm说白了是一个运行在系统中的一个进程,进程一个进程能运行一定是在内存中开辟了空间的,说了那么多,其实是要理解jvm这个软件的内存模型.
内存模型
程序运行时数据分别在5个区:
heap
- 堆的内存由-Xms指定,默认是物理内存的1/64;最大的内存由-Xmx指定,默认是物理内存的1/4。
- 默认空余的堆内存小于40%时,就会增大,直到-Xmx设置的内存。具体的比例可以由-XX:MinHeapFreeRatio指定
- 空余的内存大于70%时,就会减少内存,直到-Xms设置的大小。具体由-XX:MaxHeapFreeRatio指定。
- 线程共享
program counter register:
线程私有
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
以上描述截取自: 《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明
stack
存储基本数据类型,byte short int long boolean double float 等基本类型和引用。
每个方法执行的时候都会创建一个栈帧(stack frame)用于存放 局部变量表、操作栈、动态链接、方法出口。栈的大小可以通过-XSs设置,如果不足的话,会引起java.lang.StackOverflowError的异常.
线程私有
method area:
存储类信息,如变量,类方法,构造方法等.
线程共享
总结
名称 | 特征 | 作用 | 配置 | 异常 |
---|---|---|---|---|
栈区 | 线程私有,使用一段连续的内存空间 | 存放局部变量表、操作栈、动态链接、方法出口 | -XSs | StackOverflowError OutOfMemoryError |
堆 | 线程共享,生命周期与虚拟机相同 | 保存对象实例 | -Xms -Xmx -Xmn | OutOfMemoryError |
程序计数器 | 线程私有、占用内存小 | 字节码行号 | 无 | 无 |
方法区 | 线程共享 | 存储类加载信息、常量、静态变量等 | -XX:PermSize -XX:MaxPermSize | OutOfMemoryError |
垃圾回收机制
如何定义垃圾:已经不被使用在heap中的对象,
java通过算法来识别和清除这些占用内存的无用对象.
Mark-Sweep标记-清除算法(优点:停顿时间少,缺点是产生内存碎片)
Mark-Compact标记-整理算法(优点:无碎片内存,缺点:停顿时间长)
Generational Collection 分代收集
新生代(分三个区:Eden, from survivor, to survior。)采用复制-清理 ,多次复制后转移到老年代
老年代,(采用标记-清理)
永久代