1.JVM内存结构
方法区:存放已经加载的类信息,常量,静态变量。即永久代。在1.8中不存在方法区了,被元数据区替代了,原方法区被分为:1.加载的类信息;2.运行时常量池。加载的类信息被保存到元数据区中,运行时常量池保存在堆中。
2.Java内存模型(JMM)
(1)volatile:保证可见性和有序性;
(2)synchronized:保证可见性(在unlock前写入内存)和有序性(只能一个线程占有锁)。通过管程(Monitor)保证原子性。但是不保证禁止指令重排
代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。
(3)final保证可见性。
3.heap和stack
(1)heap由程序员自己申请,并指明大小;stack由系统分配,存局部变量。
(2)stack是连续的内存结构,默认大小2M;而堆不连续
4.栈溢出
私有,每个方法执行都会创建一个栈,当方法递归可能会出现。通过调整参数-xss调整栈大小。
5.OOM
除了程序计数器,其他都会发生OOM。
(1)栈发生stackoverflowerror,无限创建线程就会发生栈的OOM;(2)常量池在堆中,溢出会发出OutOfMemoryError。(3)堆内存溢出,报错同上;(4)方法区:动态生成大量的类,jsp
(5)直接内存OOM,涉及-XX:MaxDirectMemorySize参数和unsafe对象对内存的申请。
6.Java常量池
7.判断一个对象是否存活
(1)引用计数法,无法解决循环引用,比如A引用B,B引用A,不采用。
(2)可达性分析法:从一个GC Roots对象向下搜索,如果没有任何引用链连接就不可用。
GC Roots对象:(1)方法区静态变量;(2)JVM 栈中引用的对象(3)方法区中常量池引用的对象(4)本地方法栈引用的对象。
还需要两次标记:第一次,判断当前对象有没有finalize()方法,若不存在就记为垃圾等待回收。与GCROOTS想连接。若有的话进行第二次标记,放入一个队列,生成一个finalize线程执行,执行后仍然没有于GC Roots对象有引用就被回收。
8.四种引用
(1)强引用:如string s=new string
(2)软引用:可有可无,当内存不足就回收,回收后还是不足就抛出内存溢出
(3)弱引用:无论内存足不足都会被回收
(4)虚引用:跟踪垃圾回收对象的活动。
被引用的对象不一定存活,要看引用类型。
9.垃圾回收算法
1.标记清除法:产生不连续的内存碎片,导致大对象分配不了再触发GC
2.标记整理法:适用存活对象多,垃圾少的情况。
3.复制法:将内存分为两块;不会产生内存碎片,缺点内存适用率低。
4.分代收集法:新生代采用复制,老年代标记整理或清除。
10.垃圾回收器
年轻代(复制算法);serial,ParNew,Parallel Scavenge
老年代:标记整理(serial old,parallel old),标记清除(CMS),
年轻代和老年代:G1
11.CMS回收过程
并发标记清除以获取最短回收停顿时间为目标的收集器,在垃圾收集时使得用户线程和GC线程并发执行。
(1)初始标记,仅标记GC ROOT的下一级对象,过程中会STW,但是跟GC ROOT直接关联的对象不多,时间段;
(2)并发标记:根据上一步结果,继续向下直到尽头。过程是多线程的,耗时长,但工作线程不会阻塞,没有STW;
(3)重新标记:标记在第二步中产生的新的垃圾;
(4)并发清除:清除删掉判断已经死亡的对象,由于不需要移动存活对象,所以这个阶段是与用户线程同时并发执行的。
缺点:(1)并发回收导致CPU资源紧张;(2)无法清理浮动垃圾:在并发清理时产生的垃圾只能下一次GC;(3)并发失败,由于并发需要挤出一部分空间给并发回收的程序适用,如老年代92%就触发,而不能100%;(4)内存碎片,可以通过命令在下一次前整理
12.G1回收过程
(1)初始标记(会STW),也是只标记下一级关联的对象
(2)并发标记
(3)最终标记(会STW):对用户线程做短暂的暂停,处理并发阶段结束后任有引用变动的对象。
(4)清理阶段(STW):会更新region的回收价值和成本排序,把决定回收的一部分region复制到空,再清理。这里涉及存活对象的移动,必须暂停用户线程。
13.完整的GC
新生代1/3,老年代2/3;新生代分为Eden,to survivor,from survivor,比例8:1:1;
新生代复制,因为少量对象存活。
14.空间分配担保机制
如果young GC后存在大量对象存活,而survivor放不下,老年代也放不下。这时老年代就会先检测-xx:HandlePromotionFailure参数是否允许担保失败。允许,判断老年代最大可用连续空间是否大于晋升对象的平均大小,大于就进行young GC,小于就full GC;
15.类加载
类的生命周期:加载,验证,准备(为class对象的静态变量分配内存并初始化),解析(符号引用转换为直接引用),初始化(调用类构造器的过程),适用和卸载。其中,验证准备解析称为连接。
加载过程:(1)通过类的全限定性类名获取类的二进制流;(2)转为方法区的运行时数据结构;(3)再堆中生成一个class对象。
16.类加载器:启动类加载器(加载Java核心类库),扩展类加载器(加载Java扩展库),系统类加载器(加载我们自己写的应用类),自定义类加载器(继承自class loader)
17.双亲委派机制
防止内存中出现多个相同的字节码;因为如果没有双亲委派,用户就可以自己定义一个String类,无法保证类的唯一性。
打破:自定义类加载器,继承class loader,重写loadclass和findclass方法。
18.JVM调优命令:
(1)jsp:JVM 显示指定系统内所有的hotspot虚拟机进程。
(2)jstate:监控虚拟机运行时状态信息的命令
(3)jmap:生成heap dump文件。或者-XX:HeapDumpOnOutOfMemoryError让OOM时自动生成dump文件。还可以查询finalize执行队列,堆的适用和哪种收集器。
(4)jhat:和jmap一起适用,分析jmap生成的headdump文件,一般把dump文件复制到浏览器查看
(5)jstack:生成虚拟机当前时刻的线程快照,查看各个线程的调用堆栈