java的内存管理全部交由JVM代替实现,开发者不需要关注与内存的分配和回收
JVM按照存储内容的不同,将整个JVM内存分为5大类。
其中线程私有:方法计数器、虚拟机栈、本地方法栈;
线程共享:方法区、堆。
记录线程即将执行的指令。多线程切换时,通过它来保证线程有序的向下执行。
又名线程方法调用栈。每个栈桢存储方法的入口、引用参数、局部变量表、方法返回地址、操作数栈等。栈桢的入栈和出栈,对应着方法的调用和返回。
与虚拟机栈类似,只不过本地方法栈记录本地方法,而线程方法栈记录java方法。
存放Class对象、静态变量、常量、字符串常量等等。
存放对象的区域,根据GC回收的不同策略,又分为老年代和新生代。
- 使用句柄池,当GC移动对象时,并不需要修改引用,只需修改句柄池中的引用
- 直接使用,1:访问快,2:节省了句柄池的空间
1:在栈中创建一个引用变量a
2:在堆中创建A的实例变量
2.1:在方法区中找到A的Class对象
2.2:根据Class对象创建一个实例对象,存放在堆中
3:将新建的对象的地址赋值給变量a
JVM会自动回收垃圾对象,那么何为垃圾对象呢?计算垃圾对象有以下两种算法
记录本对象被引用的次数。当数量为0即可视为垃圾对象。
优点:实现方便,判定效率高
缺点:无法处理循环引用的情况
以GC Roots对象为出发点,计算一条对象引用链,不在该链路上的对象即视为垃圾对象。
优点:可解决循环引用的情况
缺点:实现成本高,效率不及引用计数器
GC Roots对象的类型:虚拟机栈中的变量、方法区中的静态变量、常量
首先找出垃圾对象,然后将对象占用的内存释放
优点:实现方便
缺点:产生大量的内存碎片
首先找出垃圾对象,然后将垃圾对象占用的内存释放,最后将存放的对象向内存的一边整体移动
优点:不会产生内存碎片,内存空间紧凑
步骤
1:将整个内存分为两块
2:找出存活的对象
3:将存活的对象复制到另外一个内存空间
4:清空本内存空间
5:反复执行2-4
优点:缓解了对象移动带来的损耗
将堆按照对象的存活时间分为不同的区域,不同区域使用不同的垃圾回收器
1:将堆分为老年代和新生代
2:将新生代氛围Eden区和survivor
注释:
STW:Stop The word。即在进行垃圾回收时,所有工作线程暂停工作。
新生代、单线程、STW、复制算法
新生代、多线程、STW、复制算法
新生代、多线程、STW、复制算法
追求吞吐量,即工作线程工作时间和总的时间的占比。
老年代、单线程、STW、标记-整理法
老年代、多线程、STW、标记-整理法
老年代、多线程、并发清除、标记-清除法
步骤
1:初始标记
简单标记GC Roots对象,该阶段停止工作线程
2:并发标记
对初始标记对象进行可达性分析,搜索存活对象。工作线程不停止
3:重新标记
修改并发阶段状态变化的对象
4:并发清除
清除垃圾对象,工作线程不停止
将比较耗时的并发标记和并发清除,和工作线程共同运行。减少STW停顿时间
优点:GC线程和工作线程并发工作,不会影响系统的交互性
缺点
1:GC线程和工作线程竞争CPU
2:标记-清除算法产生内存碎片
3:会产生浮动垃圾,无法回收并发线程产生的垃圾
堆空间、多线程、并发、标记-整理法
将整个对分为若干个region。垃圾回收时按照“垃圾对象空间”排序region,优先清空垃圾对象多的region。
初始标记-并发标记-重新标记-并发清除 优点:有效时间内尽可能多的回收垃圾对象。提高垃圾回收率
利用工具获取JVM内存快照、分析内存快照
1:分析各个类对应的实例变量的个数
2:分析Class数量是否过于庞大
3:线程执行情况,是否有线程长期阻塞
1:加载的类太多,可以考虑分布式拆分系统
2:可以考虑扩容JVM内存
3:注意字字符串常量
1:考虑栈容量是否太小
2:考虑是否有大量的递归调用
1:根据内存快照分析是否有内存泄漏的情况
2:分析容量