本篇主要了解一下JVM对内存的管理。
内存结构
- 方法区:也称为永久区。类的定义,常量,静态变量,即时编译器编译后的代码等存放在该区域
- 堆:主要是对象和数组数据。各线程共享资源。
- 栈:
程序运行时有一个线程在工作。就是调用程序的方法。方法中可能产生一些局部变量。对对象的引用存放在栈中。
程序运行时可能有多个线程,因此它们的变量不能放在一起,必须分开存放。因此栈空间是线程私有的。
栈帧:存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。 - 程序计数器:保存当前执行的位置
- 本地方法栈:JVM调用本地方法时存放的位置
堆的结构
堆中还有一些更小的区域,比如新生代,老年代,永久代。
现在永久代中也会进行垃圾回收。
新生代和老年代根据存在时间的长短进行划分。对象刚刚产生的时候存放在伊甸园(Eden Space),一段时间后垃圾回收器会检查新对象,比较重要的对象留下来,移到From区(From Space)。过段时间以后再进行回收。经过若干次(默认15次)回收之后如果一个对象还存活,将会被移动到老年代(Tenured Space)中。
垃圾回收
程序员写Java的时候不需要考虑内存的回收,不会有内存泄漏的发生。
垃圾回收算法1
思路:如果对象B被对象A引用,则对象B将不被回收。
问题:如果两对象存在相互引用,它们永远不会被释放。
垃圾回收算法2:根节点引用
思路:从程序中根节点开始遍历。凡是不可到达的节点将被回收。
清除方法
标记清除
问题:采用标记删除的方法会产生内存碎片,可能导致后续的大对象无法保存
标记整理
问题:移动对象可能会影响性能
复制算法
思想:事先留一些内存空间,最多只用一半的空间,一旦发生需要垃圾回收时,将对象复制到预留空间中,然后将原先的一般清除掉,然后再复制回去。(From Space 和 To Space称为Survivor区,大小与Eden Space的比例是1:4)
需要垃圾回收时大部分的对象都是垃圾,所以不需要预留一半的空间,只需要留一小块就可以了。
垃圾回收器
Serial收集器:
- 单线程的收集器,”Stop the world”
- 对于运行在Client模式下的虚拟机来说是一个很好的选择
- 简单而高效。
新生代用复制算法,老年代用标记整理。
还没被淘汰哦
Serial Old
针对老年代的单线程收集器,标记-整理
ParNew收集器
- Serial收集器的多线程版本
- “Stop the world”
Parallel Scvenge收集器
- 吞吐量优先
- 新生代收集器,复制算法,并行的多线程收集器
- 并行(Parallel):多条垃圾收集线程并行工作,但此时用户线程处于等待状态
- 并发(Concurrent):只用户线程和垃圾收集线程同时执行
- 经常工作
CMS收集器
- 目标:取得最短的回收时间。用于服务端
- 标记-清除
- 与客户代码并发执行
- 4个步骤:初始标记,并发标记、重新标记、并发清除
- 优点:并发收集,停顿低
- 缺点:对CPU资源非常敏感,无法清除浮动垃圾
G1收集器
- 面向服务端应用的垃圾收集器
- 优点:并行与并发、分代收集、空间整合、可预测的停顿
- 步骤:初始标记、并发标记、最终标记、筛选回收
使用指定的收集器
-XX +UseSerialGC