01-jvm内存结构-代码执行流程
![](https://img-blog.csdnimg.cn/img_convert/c30dff1bda82bb613376e1cdd5c679d0.png)
java源代码对应java source
javap将源代码编译成java class字节码(支持跨平台,可以被虚拟机解释为使用于各个平台的机器码)
接下来图中所剩下的部分都可以称为java虚拟机的一部分。使用java命令执行字节码后,就会创建出java虚拟机。
生成main主线程执行方法,此线程需要的内存由虚拟机分配(所有创建的线程所需的内存都是来自于虚拟机栈)。
主线程碰到一个没见过的类Main,把Main这个类通过类加载子系统把类的原始信息加载到方法区(把字节码文件读取到内存里来)。
接下来开始执行main方法,又碰到一个没见过的类Student,把Student的类信息加载到方法区。(student不是成员变量,而是方法局部变量,所以上面Main类加载的时候是没有加载到方法区的)。
遇到一个new,计算对象创建所需要的内存空间,创建对象然后放到堆中的空闲处。
stu这个局部变量跟方法参数args,它们都是引用地址,引用对象的内存都是放在虚拟机栈中。
方法分为两类,一类是java可以实现的,比如上面的study方法。另外一种是需要调用操作系统底层函数实现的比如hashcode方法。java方法内存用的虚拟机栈,本地方法用的本地方法栈。
main线程代码执行到一半,可能要交出cpu使用权,那下次再回来时怎么知道代码从哪里继续?这就需要用到程序计数器,记录当前代码执行到哪行代码。
stu=null,没有对象引用的对象可以进行回收
机器并不认识字节码,这就需要通过解释器把机器码翻译为机器码
调用比较频繁的代码称为热点代码,就不适合用解释器解释,会通过即时编译器翻译为机器码并缓存。
02-jvm内存结构-哪些区域会有内存溢出
![](https://img-blog.csdnimg.cn/img_convert/eacf6fc1ea1680f6e2f6c9504d02eeda.png)
03-jvm内存结构-方法区-元空间
![](https://img-blog.csdnimg.cn/img_convert/c0c336293a6ea7fd800c76e3fe61e5c5.png)
方法区是规范。永久代和元空间是不同版本的虚拟机对方法区的实现
![](https://img-blog.csdnimg.cn/img_convert/8dc90987a47ffdb2cbb4fad123cec217.png)
类的基本信息存储在方法区(类加载时放入),同时也会在堆内存创建出一个X.Class对象。我们访问类的原始信息不能直接访问,需要通过X.Class对象间接访问。
![](https://img-blog.csdnimg.cn/img_convert/04aff7af70c0dfa31a7e09beaab4a519.png)
什么时候方法区里类的信息内存会得到清理呢?如上图,虽然Y.Class对象和c对象都没有被引用了,但是方法区中类Y不能被卸载。只有整个类加载器不再使用了,那这个类加载器所加载的类才会被卸载。
![](https://img-blog.csdnimg.cn/img_convert/21e115bbf8762f4d848a20d8e45284d0.png)
04-jvm内存参数
堆内存参数
![](https://img-blog.csdnimg.cn/img_convert/75dff8bba887cbedd638822fe8487e2e.png)
-Xmx 最大堆内存
-Xms 最小堆内存
-Xmn 新生代内存
-XX:SurvivorRatio=3 eden:from=3:1
from所占内存=to所占内存
Survivor内存=from内存+to内存=2G
![](https://img-blog.csdnimg.cn/img_convert/44df98eac6e86d9333a7a7f2c18aeab2.png)
![](https://img-blog.csdnimg.cn/img_convert/9c6f9fca4590c4df87a7c51d5faa94d1.png)
![](https://img-blog.csdnimg.cn/img_convert/10190279dc300aaef0f0b2e288504c9c.png)
![](https://img-blog.csdnimg.cn/img_convert/77a89646dc5b6751619d5ecbec5803d6.png)
05-jvm垃圾回收算法
![](https://img-blog.csdnimg.cn/img_convert/2f95117ffaff3e3db99cb3c03516629e.png)
标记不能被垃圾回收的对象,垃圾回收发生时标记的对象把它保留下来,未标记的就清理掉。
1、标记清除
![](https://img-blog.csdnimg.cn/img_convert/161abfd6a6284b3fb55bfc2ed2456f2f.png)
GC Root:局部变量引用的对象、静态变量引用的对象
缺点:内存碎片
2、标记整理
无内存碎片,但是效率低
3、标记复制
![](https://img-blog.csdnimg.cn/img_convert/e47b77081b65332b973327582608ff8d.png)
效率相对于标记整理更好。因为清除较快,直接把幸存对象复制到另一片区域,原区域作废。(适合新生代,因为新生代存活对象较少,复制的过程很快,而老年代存活的对象较多,全部复制的话效率较低,老年代更适合标记整理)
缺点:内存占用较高
06-jvm垃圾回收-概述
面试题:说说GC和分代回收算法
![](https://img-blog.csdnimg.cn/img_convert/214c1825f52eb30770266d1ceedb2810.png)
07-jvm垃圾回收-分代回收
![](https://img-blog.csdnimg.cn/img_convert/abc1ad5a2e590fabc254a35e6b26e06a.png)
![](https://img-blog.csdnimg.cn/img_convert/9837ba7492e02f558701b270f537a00e.png)
![](https://img-blog.csdnimg.cn/img_convert/abd3dbd57a61b60240cbc542be23cbe4.png)
![](https://img-blog.csdnimg.cn/img_convert/3220e2e9d1a7bf0cece77ab9b21e1d0a.png)
![](https://img-blog.csdnimg.cn/img_convert/590b08072a9cef8cca59d8cb8ab51a09.png)
![](https://img-blog.csdnimg.cn/img_convert/35539c4ff590e94f129ab7c67eed4b61.png)
08-jvm垃圾回收-三色标记
![](https://img-blog.csdnimg.cn/img_convert/37ef86dc065b4325537a307b9325a8e2.png)
黑色:沿着根对象已经找到了你这个对象并且这个对象的引用也在处理了。
灰色:沿着根对象已经找到了你这个对象,但是这个对象的引用还没有处理完成。
![](https://img-blog.csdnimg.cn/img_convert/38d7db035d348564cad1b1564f18995b.png)
![](https://img-blog.csdnimg.cn/img_convert/6d979078a8670461f9a7f05078f537f0.png)
![](https://img-blog.csdnimg.cn/img_convert/b806d08966a568baf528647ea4eaa8cd.png)
09-jvm垃圾回收-并发漏标
![](https://img-blog.csdnimg.cn/img_convert/b60259d8b2d0780b6bc4fcf438dd7467.png)
10-jvm垃圾回收-垃圾回收器
![](https://img-blog.csdnimg.cn/img_convert/6a712116b1b00898e480fba98b51f732.png)
新生代:标记复制,老年代:标记整理
![](https://img-blog.csdnimg.cn/img_convert/51c274421a20273f4f80a4d0ee2bc500.png)
老年代垃圾回收器。并发失败(产生对象的速度>垃圾回收的速度),会进行full gc
11-jvm垃圾回收器-G1
![](https://img-blog.csdnimg.cn/img_convert/7b4197fc89dd614ef56b83096e4256f0.png)
![](https://img-blog.csdnimg.cn/img_convert/b73d0b18c796aace487639d432d3f692.png)
![](https://img-blog.csdnimg.cn/img_convert/f1a742a77c9efcb6c52ffe08c2da45a2.png)
随着这些eden区放满,会触发新生代的垃圾回收。为啥不多创建几个eden?eden大小会受新生代大小控制的。而G1新生代的内存占比是在5%~6%之间波动,所以eden就不能随便创建新的了。
![](https://img-blog.csdnimg.cn/img_convert/35131a58c29b5a92deb6be5079e8e410.png)
![](https://img-blog.csdnimg.cn/img_convert/4ab58224b37e10c5b8e0d083ef4e102d.png)
![](https://img-blog.csdnimg.cn/img_convert/9ee4d25833a8c06f4f3a8b2a476606a5.png)
![](https://img-blog.csdnimg.cn/img_convert/cea4ae2784fcb95fb41a4cb9bea51b82.png)
当老年代的内存超过一定阈值就会触发并发标记,默认是老年代内存达到了堆内存的45%以上。
![](https://img-blog.csdnimg.cn/img_convert/4892bc076a7eb43454eef839ca5ab84d.png)
混合收集会根据暂停时间,优先对回收价值高的区域(存活对象少的区域)进行回收
![](https://img-blog.csdnimg.cn/img_convert/690caacac68feb51bd871b4854df7504.png)
13-内存溢出-情况1-误用固定大小线程池
![](https://img-blog.csdnimg.cn/img_convert/b6d31ab0f9a54a2c0b1c0056e12b82a7.png)
![](https://img-blog.csdnimg.cn/img_convert/c3b8822451b402bf21bfab14d167690c.png)
![](https://img-blog.csdnimg.cn/img_convert/5d142d9a62e268a3fb3a03098624714b.png)
解决方法:不要用自带的线程池,用自己定义的线程池,设置一个有
大小限制的任务队列。
14-内存溢出-情况2-误用带缓冲线程池
![](https://img-blog.csdnimg.cn/img_convert/b8b62a9902afc509b4b4274498ac9c15.png)
救急线程没有上限,线程数耗尽了系统的线程资源
![](https://img-blog.csdnimg.cn/img_convert/1e490d399c85b8430c035e16371c52bf.png)
![](https://img-blog.csdnimg.cn/img_convert/a456963bd3cee7b7e350ba4d374ecca8.png)
15-内存溢出-情况3-一次查询太多数据
16-内存溢出-情况4-类太多
![](https://img-blog.csdnimg.cn/img_convert/4684d7eb4aebef29c1eeb1e334ea1775.png)
![](https://img-blog.csdnimg.cn/img_convert/4c49b98bbfbf1ab1ea8c23b34baf2388.png)
17-类加载-三个阶段
![](https://img-blog.csdnimg.cn/img_convert/b075fc78895d364d7fcb57f8b2dbb3b0.png)
18-类加载-验证类加载是懒惰的
![](https://img-blog.csdnimg.cn/img_convert/e322cb51962353bbc888122b12f3b4fe.png)
![](https://img-blog.csdnimg.cn/img_convert/9343fd76a6c0b05b0a448a0c89c3d67f.png)
(调用类的静态成员变量也会触发类的初始化)
19-类加载-验证类对象位于堆
20-类加载-验证类静态变量在初始化时赋值
![](https://img-blog.csdnimg.cn/img_convert/19b39843bc421798a4c6b32132637a32.png)
21-类加载-如何找到类对象地址
22-类加载-类初始化方法原理
![](https://img-blog.csdnimg.cn/img_convert/d85573c0c5b10e791d854e1b52488419.png)
![](https://img-blog.csdnimg.cn/img_convert/606ec6dae63e592a7e7a622a7f508b38.png)
这个方法在类的初始化的时候被调用
![](https://img-blog.csdnimg.cn/img_convert/2e59fe42ae040faca6843a504af1d09a.png)
![](https://img-blog.csdnimg.cn/img_convert/d8c9162338a0503208cec8a1a36e2609.png)
![](https://img-blog.csdnimg.cn/img_convert/f201388be5bc9ba5e914639ec3a1df78.png)
23-类加载-final修饰基本类型变量的原理
![](https://img-blog.csdnimg.cn/img_convert/ef2477b17901639be789e77cf88a7f7d.png)
看TestFinal的字节码
![](https://img-blog.csdnimg.cn/img_convert/dff51f2c5138f5d5e3d5941acd925576.png)
对于final修饰的基本数据类型,用到的类就会把这个值复制到自己的类中(即把这些值复制了一份放到了TestFinal类中)
(如果数字比较小直接放在方法区,如果数字比较大直接放到常量池中)
24-类加载-将符号引用变为直接引用
研究第二阶段---解析
![](https://img-blog.csdnimg.cn/img_convert/d5fa6519d80b0d8bc120cf3b3c669e53.png)
第一个read时三个类都还没被加载
![](https://img-blog.csdnimg.cn/img_convert/1f4443cd86f12b7620711e3a5923bdb5.png)
第二个read时
![](https://img-blog.csdnimg.cn/img_convert/c2f0710496e0bc16a459712ef05fa961.png)
25-类加载-双亲委派
![](https://img-blog.csdnimg.cn/img_convert/f6908fef0629a65d3304f7e860f0ae0a.png)
jdk8中类加载器
![](https://img-blog.csdnimg.cn/img_convert/900f61a2a7941b45afd301b6905901ea.png)
比如要加载String.class这个类,先问Application ClassLoader有没有这个类,发现没有,然后问Extension ClassLoader,也没有,最后问Bootstrap ClassLoader,把String.class加载到内存中。上级加载的类,所有的下级也可见。
再比如加载自己写的Student.class类,虽然Application ClassLoader中有这个类,但是也得先问上级Extension ClassLoader----->Bootstrap ClassLoader,上级都没有自己才可以加载这个类,并且加载的这个类上级不可见。
26-类加载-能假冒一个System类吗
![](https://img-blog.csdnimg.cn/img_convert/d7d0d546cc0bf28d998fe6e5a823637b.png)
![](https://img-blog.csdnimg.cn/img_convert/c614cd35e3f26f9d613d1bcccf371934.png)
27-四种引用-概述
![](https://img-blog.csdnimg.cn/img_convert/ad10984aeb651752dbf9772d7ae74122.png)
28-四种引用-虚引用
![](https://img-blog.csdnimg.cn/img_convert/85191ba0971396d834f80b287a6ffff5.png)
![](https://img-blog.csdnimg.cn/img_convert/a31d858c66e1c9cb5fe714f4422b1b7f.png)
![](https://img-blog.csdnimg.cn/img_convert/67799cf264740c098f7667683463fe71.png)
![](https://img-blog.csdnimg.cn/img_convert/75dd631e94183031ae60576d289aa966.png)
如果改成"b",则不会被垃圾回收,因为这样的话是在字符串常量池中的引用,这个引用是强引用。
29-四种引用-弱引用
ThreadLocalMap中Entry的key是弱引用,而key不是。如果垃圾回收会把key回收,value不会回收。如果使用不当会造成内存泄漏。
![](https://img-blog.csdnimg.cn/img_convert/9e5a7acc7ee7468088f700beeac89023.png)
![](https://img-blog.csdnimg.cn/img_convert/9ee19d0d0c70c6076b77499c15f9c49f.png)
![](https://img-blog.csdnimg.cn/img_convert/eaec285c73de9724d9c6020ef67f4692.png)
jdk中没有使用这种方法,因为这样成本有点高。
30-四种引用-Cleaner
![](https://img-blog.csdnimg.cn/img_convert/013afa67bbe0bf65558a37f8c7a28d02.png)
为什么最后要System.in.read()?因为后台这个Cleaner-0这个线程是一个守护线程,如果主线程停止了,守护线程也会直接停止,可能来不及做清理操作。
31-finalize概述
![](https://img-blog.csdnimg.cn/img_convert/03f5d5a30bdee7176566866eabf95633.png)
![](https://img-blog.csdnimg.cn/img_convert/ba23314f52ad1667e15101e4a8905807.png)
第一,从表面上我们能看出来finalize方法调用次序并不能保证(与垃圾回收的顺序有关,因为是按照入队的顺序来调用)
第二,日志中的Finalizer表示输出日志的线程名称,从这我们看出这个叫做Finalizer的线程调用的finalize方法
第三,你不能注释掉System.out.read(),否则会发现(绝大概率)并不会有任何输出结果了,从这我们看出finalize中的代码并不能保证被执行。
第四,如果将finalize中的代码出现异常,会发现根本没有异常输出
![](https://img-blog.csdnimg.cn/img_convert/9b48748be1b77617f6e95a787fcfaf1f.png)
第五,垃圾回收时不会立刻调用finalize方法,会先把对象加到referqueue,referqueue里有一个线程,起一个调一次。
32-finalize-unfinalized链表
![](https://img-blog.csdnimg.cn/img_convert/8f3ebf4f1d7042d06b2f195d769f2e8b.png)
先调父类构造
![](https://img-blog.csdnimg.cn/img_convert/e800454974c47cc592aa79a02f057983.png)
把传入的狗对象包装成一个Finalizer对象,这个对象和虚引用弱引用对象类似,通过一个引用队列,就可以跟踪狗对象的垃圾回收过程。
![](https://img-blog.csdnimg.cn/img_convert/ac39b04289d9a1d800350bfced1a2792.png)
![](https://img-blog.csdnimg.cn/img_convert/38f00c7d6e6eca66be19fa43f9aef5d4.png)
unfinalized是一个双向链表,里面是Finalizer对象,这些Finalizer对象又真正引用了狗对象。作用是所有实现了finalize方法的对象都会放到这个链表中。
![](https://img-blog.csdnimg.cn/img_convert/a56a564784a62153477e7f0ff7ce51e5.png)
33-finalize-调用原理
第一次垃圾回收时实现了finalize方法的对象无法被回收,只有执行完finalize方法后,第二次垃圾回收后该对象才会被回收。
![](https://img-blog.csdnimg.cn/img_convert/09880d8cab072b1bfcae57a16945a146.png)
![](https://img-blog.csdnimg.cn/img_convert/a4edb7b5580669fbd4c7976cedfaacd7.png)
![](https://img-blog.csdnimg.cn/img_convert/871d68b7f82cc2138ab726a32a3aaa35.png)
![](https://img-blog.csdnimg.cn/img_convert/cd06753d9af8e5489a64deb0765fa058.png)
![](https://img-blog.csdnimg.cn/img_convert/f197e390219f87385ec9c2575efd1afb.png)
ps:finalize线程优先级并不低,普通线程优先级为5,它为8
![](https://img-blog.csdnimg.cn/img_convert/9136357f32a530b37c0eca126ef9bd36.png)
![](https://img-blog.csdnimg.cn/img_convert/d4a253378b3ab6e067c248dbe06e5d55.png)
(串行加锁一个个执行,非常慢)