JVM(随手笔记)

谈谈对jvm的理解?
java8虚拟机和之前的变化更新?
什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?
jvm的常用调优参数有哪些?
内存快照如何抓取,怎么分析Dump文件?
谈谈jvm中类加载器?rt-jar

jvm位置
在这里插入图片描述
1、类加载器
作用:加载Class文件(new Student()具体实例,引用放在栈中,具体的人放在堆中)
类是抽象的(模板),而对象是具体的。
(1)虚拟机自带的加载器
(2)启动类(根)加载器
(3)扩展类加载器
(4)应用程序加载器

双亲委派机制
1、类加载器收到类加载的请求
2、将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
3、启动类加载器检查是否加载当前这个类,能加载就使用当前的加载器,否则,抛出异常,通知子加载器进行加载
4、重复步骤3
App—>EXC—>BOOT(最终执行根加载器)
参考链接(https://www.jianshu.com/p/1e4011617650)
沙箱安全机制
参考链接(https://blog.csdn.net/qq_30336433/article/details/83268945)

native关键字
凡是带了native关键字,说明java的作用范围达不到了,会去调用底层C语言的库,会进入本地方法栈,调用本地方法接口(JNI)
JNI作用:扩展java的使用,融合不同的编程语言为java所用。最初:C、C++
*(java诞生的时候,C、C++横行,想要立足,必须要有调用C、C++
它在内存专门开辟了一块标记区域:Native Method Stack。登记native方法,它最终执行的时候,加载本地方法库中的方法通过JNI)
*

PC寄存器
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即是即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

方法区(Method Area)
方法区是被所有线程共享的,所有字段和方法字节码,以及一些特殊方法,如:构造函数、接口代码也在此定义。简单来讲:此区域属于共享区间
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中和方法区无关
static、final、Class、常量池

**栈:**先进后出
main压栈,其次是方法压栈,执行完毕弹出。直到所有方法都执行完,main方法才弹出。
栈溢出:递归调用
栈:栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题。
一旦线程池结束,栈就Over
栈:8大基本类型+对象引用+实例的方法
栈运行原理:程序在执行的时候一定是在栈顶

栈、堆、方法区的交互关系:
一个对象实例化的过程在内存中
队列:先进先出

三种JVM
1、Sun公司 HotSpot


Heap ,一个JVM只有一个堆内存,堆内存的大小可以调节。
类加载器读取类文件后,一般把什么文件放在堆中?类、方法、变量、常量等,保存我们所有引用类的真实对象。
堆中还要细分三个区域:
1、新生区(伊甸园区)
类:诞生和成长的地方,甚至死亡;
伊甸园区,所有的对象都是在伊甸园区new出来的
幸存区:
经过研究,99%的对象都是临时对象
2、养老区:新生区满了之后经过轻GC之后,活下来的进入到养老区
3、永久区
这个区域是常驻内存的,用来存放jdk自身携带的Class对象,存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收,关闭虚拟机就会释放这个区域的内存。
一个启动类加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出翔OOM
jdk1.6之前:永久代,常量池在方法区
jdk1.7: 永久代,但是慢慢退化了(去永久代),常量池在堆中
jdk1.8之后:无永久代,常量池在元空间(元空间逻辑上存在,物理上不存在)
在这里插入图片描述
heap调优
在这里插入图片描述 在这里插入图片描述
GC垃圾回收主要在伊甸园区和养老区,假设堆内存满了(会报OOM),堆内存不够!
JDK8以后,永久存储区改了名字(元空间)

如果在一个项目中,突然出现OOM故障,那么该如何排错?
1、能够看到代码第几行出错:内存快照分析工具,MAT、Jprofiler
2、Debug,一行行分析代码

MAT、Jprofiler作用:
分析Dump内存文件,快速定位内存泄漏
获得堆中的数据
获得大的对象
dump文件

GC垃圾回收

GC的作用区域
在这里插入图片描述
JVM在进行GC时,并不是对这三个区域统一回收,大部分时候回收是在新生代
新生区
幸存区
老年区
GC两种类:轻GC(普通的GC),重GC(全局GC)

GC题目:

JVM的内存模型和分区,详细到每个分区放什么?

堆里面的分区有哪些?Eden、from、to、老年区,说说他们的特点?

GC的算法有哪些?标记清除法、标记压缩、复制算法、引用计数法,怎么使用的?
复制算法
好处:没有内存的碎片
坏处:浪费了内存空间,多了一半空间永远是空的,to
复制算法最佳使用场景:对象存活度较低的时候(新生区)
在这里插入图片描述
在这里插入图片描述
标记清除算法
在这里插入图片描述
优点:不需要额外的空间
缺点:两次扫描严重浪费时间,会产生内存碎片。

标记压缩算法
防止内存碎片产生,再次扫描,向一端移动存活的对象,但是多了一个移动成本
先标记清除几次在这里插入图片描述
再压缩
在这里插入图片描述
总结
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内部整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法

难道没有最优的算法吗?
答案:没有,没有最好的算法,只有最合适的算法—>GC:分代收集算法

年轻代:
存活率低,复制算法
老年代:
区域大:存活率
标记清除(内存碎片不是太多)+标记压缩混合实现

轻GC和重GC分别在什么时候发生?
Young GC的触发时机:Young GC其实一般就是在新生代的Eden区域满了之后就会触发,采用复制算法来回收新生代的垃圾。
Full GC的触发时机如下:
(1)发生Young GC之前进行检查,如果“老年代可用的连续内存空间” < “新生代历次Young GC后升入老年代的对象总和的平均大小”,说明本次Young GC后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间
此时必须先触发一次Old GC给老年代腾出更多的空间,然后再执行Young GC。
(2)执行Young GC之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必须立即触发一次Old GC。
(3)老年代内存使用率超过了92%,也要直接触发Old GC,当然这个比例是可以通过参数调整的。
概括成一句话,就是老年代空间也不够了,没法放入更多对象了,这个时候务必执行Old GC对老年代进行垃圾回收。

原文地址:https://www.cnblogs.com/kiki-study/p/13656120.html