深入学习JVM虚拟机
# 栈、堆
在冯诺依曼结构的计算机里面,程序运行的时候,cpu会把程序load到内存,形成一个进程。
在内存里面,每个方法就会形成一个栈,每一行指令,都会放到栈里面,先进后出。
然后当我们新建一个对象,栈会存储一个指针,指针指向堆空间。
当我们用完一条指令,栈就会弹出最上层,于是栈空间天然是自动回收的。
而被指令指向的堆空间,则是没有自动维护。于是,GC就诞生了。
# GC发展史
- C/C++
- 手工管理,容易内存泄露
- java python go
- 引入虚拟机,自动管理
- rust
# 什么是垃圾,怎么标记?
python使用了引用计数法 reference count,没有东西引用它,就是垃圾
但是如果有好几个对象相互引用,那么就无法清除这个垃圾。
Java使用了根到底算法。 也就是从根方法开始访问,访问的到的不是垃圾,访问不到的就清除掉
# 常见的垃圾回收算法和垃圾回收器
发展几十年了,还是这三种
标记清除,标记到就清除掉,但是缺点就是会导致内存碎片化
复制,浪费内存
mark-compact 边清除边整理,效率低
三种垃圾回收算法都有问题,所以是综合使用的
Java常见的垃圾回收器有十种
前面六种是分代的,G1按理来说也是分代的。
GC随着内存的大小不断增长而演化
# 新生代和老年代该用什么算法回收
1.8 以及以前,都把内存分成两块,新生代和老生代。
刚创建的对象就放在了新生代,随着垃圾回收次数增多,还没有被回收的话,就会根据适当的规则放进老生代。
老生代的垃圾回收次数比较少,减少了性能的损耗。
新生代按照 8:1:1来分配为 eden 和 survivor 1、 survivor 2
每扫一次eden,90%都会被回收。然后进行copying算法,把剩余的对象复制到survivor 1
下一次eden清理的时候,会把survivor1也清除,剩余的放在survivor2
再下一次 就是eden survivor2一起扫,剩下的放servivor1,就这样循环。
ps + po 是jdk默认的垃圾收集器
Serial 和Serial Old
单线程,stop-the-world,一旦执行就会全部停止。适合小内存的情况
Parallel Scavenge 并行回收 年轻代 Parallel Old 并行回收 老年代 适合几十兆到几百兆1G的内存,同时也是1.8默认的垃圾回收器
几十G的内存 使用concurrent GC 并发GC ParNew + CMS 是最早的实现。
# cms 使用三色标记法
# 调优实战
# 常见命令
输入java 就会启动一个java虚拟机
以横杠开头的是标准参数
以-X开头的是非标参数
实际上,以-XX为开头的参数才是最终会用到的调优参数
用这个命令可以输出
java -XX:+PrintFlagsFinal -version
-XX:+PrintGC 打印GC信息在控制台
jps jinfo jstat 统计信息 -gc 统计GC 信息 jstack
排查cpu升高,先用TOP查看进程 然后使用 TOP -HP pid查看线程的cpu 根据线程编号,使用jstack
如果是GC线程,那就看GC信息,如果是业务线程,那就看业务逻辑
jmap 转储堆栈文件 jmap -histo jpid | head -20 jmap -dump:format=b,file=22222.hprof jpid