深入理解java虚拟机笔记:自动内存管理

本文详细解析了Java虚拟机的工作原理,包括CPU架构、运行时内存区域、不同引用类型、垃圾收集算法及Hotspot虚拟机的垃圾收集器。同时介绍了如何通过命令行工具监控和调试Java应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虚拟cpu

cpu可分为基于寄存器的和基于栈的两种。x86是基于寄存器的cpu,而java虚拟机是一种基于栈的(虚拟)cpu,要运算的数据存放在操作数栈(Operand Stack),指令一般不需指明数据的位置,比如iadd指令就是把栈顶两个整数弹出并把相加结果入栈。Android Dalvik虚拟机是基于寄存器的,但可以执行java字节码。

运行时内存区域

程序计数器
每个线程有一个,指向下一条要执行的指令。
虚拟机栈
也是线程私有的,每进入一个方法就会创建一个栈帧(Stack Frame)。每个栈帧里面又会包含局部变量表、操作数栈、动态链接、返回地址等。其中每个部分的大小是在编译期确定的,即一个栈帧的大小是在编译期确定的。局部变量表的第一个slot里存放的就是this指针。
本地方法栈 java堆 方法区
存放已加载的类信息。也被成为永久代(Permanent Generation),可用-XX:maxPermSize指定大小。方法区里面有一块被称为运行时常量池(Runtime Constant Pool),存放常量(比如intern的String)。如果动态生成的类太多,则会造成永久区溢出:OutOfMemoryError: PermGen Space。-XX:MaxPermSize=64M可以满足绝大多数应用的需求,如果还不够就设成128M,如果还是不够就应该重构系统,比如使用专门的classloader等。(如果一个类的所有对象都已经被销毁,而且该类的classloader也被销毁,则该class会被回收。而常量池会自动回收。)
直接内存
用于NIO,文件通道。

不同引用类型

  • StrongRefence。
  • SoftReference在内存将满时会被回收,适合用来实现cache。
  • WeakReference只要被垃圾回收器发现就会被回收,即最多生存到下一次垃圾回收之前。
  • PhantomReference(虚引用)最弱的引用,仅用来跟踪垃圾回收过程。

垃圾收集算法

标记-清除算法
标记要回收的对象,然后释放,会产生很多碎片。
复制算法
将内存分两半,将活着的对象复制到另一半。适合新生代,新生代98%的对象时朝生夕死的,所以不需要1:1的比例。Hotspot虚拟机默认是8:1(-XX:SurvivorRation=8),大的那一块叫Eden(一次gc都还没经历的对象),小的那块叫Survivor,Survivor又平分为S0和S1。新创建的对象放在eden区,如果经历一次gc之后还活着,则进入s0或s1。s0和s1是对等的,每次gc之后交换角色。新生代=Eden+S0+S1,新生代的大小-Xmn128m。
标记-整理算法
适用于老年代,类似标记-清除算法,会移动对象以避免碎片。
分代收集算法
将对象划分为新生代(Young generation)和老年代(Tenured generation),使用不同的回收算法。如果一个对象经过多次gc仍然存在,则进入老年代。新生代采取复制算法(包括Serial,ParNew,Parallel Scavenge收集器),老年代采取复制-整理算法。

hotspot使用的垃圾收集器


图片来源 https://blogs.oracle.com/jonthecollector/entry/our_collectors,连线表示两个收集器可以配合使用。
Serial收集器
client模式下新生代的默认收集器,单线程,stop the world。
ParNew收集器
Serial的并行版本,stop the world。在server模式下,如果老年代使用CMS,则新生代首选ParNew。
Parallel Scavenge收集器
关注最大停顿时间和吞吐量,stop the world。吞吐量=用户代码时间/(用户代码+垃圾回收时间)。会自定调整新生代大小等参数。停顿时间短适用于交互程序;吞吐量大则适用于非交互程序。
Serial Old收集器
Serial的老年版本,标记-整理算法,stop the world。Client模式下的默认收集器。
Parallel Old收集器
Parallel Scavenge的老年版本,标记-整理算法,stop the world。
CMS(Concurrent Mark Sweep)收集器
以最短停顿时间为目标,标记-清除算法,适用于B/S服务器。可以与用户线程并行工作,即不需要stop the world。
G1收集器
最新的收集器,有待时间检验。

查看GC日志

参数 -verbose:gc -XX:+PrintGCDetails

[GC [DefNew: 3324k->152K(3712K), 0.002 secs] 3324K->152K(11094K), 0.003 secs]

其中里层括号表示DefNew(也就是Serial)收集器的回收情况,回收前->回收后(总大小)。外层表示总的回收情况。新生代一般称为Minor GC,老年代称为Major GC或Full GC,老年代GC时一般也会伴随Minor GC。

内存分配与回收策略

对象优先在Eden分配
-Xmn10M设置新生代大小,-XX:SurvivorRation=8设置新生代中Eden与Survivor的比例。
大对象直接进入老年代
-XX:PretenureSizeThreshold=3145728大于此数值的对象直接分配在老年代。
长期存活对象进入老年代
-XX:MaxTenuringThreshold=15年龄超过此数值的对象进入老年代,默认15。
动态年龄判定
如果在survivor中的相同年龄的对象大小占了survivor空间的一半以上,则此年龄以上的对象直接进入老年代,而无需等待MaxTenuringThreshold。
空间分配担保
-XX:+HandlePromotionFailure。Minor GC时,如果survivor空间不够,就会借用老年代的空间;如果老年剩余空间也不够,就会进行full GC。

调试工具

jps查看java进程号

jstat

查看虚拟机的统计信息,如各空间大小,垃圾回收情况,类加载情况等。

jinfo

查看或修改虚拟机的运行参数、环境变量等。

jmap导出java堆

jmap -histo pid 查看对象统计信息。

jmap -dump:format=b,file=dump.bin pid 导出堆。

jhat

在浏览器中查看dump文件。

jstack

查看调用栈,可用来检查死锁。

hprof

查看各函数的cpu使用率

JConsole

图形化工具,监测堆、线程、死锁等信息

VisualVM新一代图形化工具

目前主推的一站式监控工具,基于netbeans开发,all-in-one。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值