之前又一遍文章【深入学习Java虚拟机】不大完整:[https://blog.csdn.net/Tony666688888/article/details/135466362\]请结合这篇在仔细研读一下:
首先我们带着这几个额问题来读这篇文章,可能收获会更大哦
1.请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?
2.什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?
3.JVM的常用调优参数有哪些?
4.内存快照如何抓取,怎么分析Dump文件?知道吗?
5.谈谈在JVM中,你对类加载器的认识?
1.类的加载,连接(验证、准备、解析)与初始化。
类的加载指的是将类的 .class 文件中的耳机子数据读入到内存中,将其放在运行时数据去的方法区内,然后再兑取创建一个java.lang.Class 对象,用来封装类在方法区内的数据结构。
1.ClassLoader(类加载器)
2.JVM提供了三总类加载器
1)根类加载器(使用C++编写,程序员无法在Java代码中或得该类)
2)扩展加载器,使用Java代码实现
3)系统加载器(应用加载器),使用java代码实现
3.用户自定义的类加载器
• java.lang.ClassLoader的子类
• 用户可以定制类的加载方式
4.程序中对子类的"主动使用"会导致父类被出释怀;但对父类的"主动"使用并不会导致子类的初始化
5.主动使用(六种)
-- 创建类的实例
-- 访问某个类或接口的静态变量,或者对该静态变量赋值
-- 调用类的静态方法
-- 反射(如Class.forName("com.itgwl.Test")
-- 初始化一个类的子类
-- Java虚拟机启动时被标明为启动类的类(Java Test)
• 除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化
6.父子加载器并非继承关系,也就是说子加载器不一定是继承父加载器。
1.JVM的位置
2.JVM的体系结构
3.类加载器
4.双亲委派机制
5.沙箱安全机制
6.Native
7.PC寄存器
8.方法区
9.栈
10.三种JVM
11.堆
12.新生区、 老年区
13.永久区
14.堆内存调优
15.GC 垃圾回收机制 (常用算法)
16.JMM
17.总结
一、 组成沙箱的基本组件:
1.字节码校验器:会
2.类装载器:
它防止恶意代码去干涉善意的代码; //加载器双亲委派机制(为了安全)
它守护了被新人的类库边界;
它将代码归入保护域,确定了代码可以进行哪些操作。
PC寄存器:每个线程都有一个程序计数器,是线程私有的,就是一个指针。
方法区:(static, final,Class,常量池)方法区是被所有的线程共享,此区域属于共享区间,静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关。
为什么main()先执行,最后结束~
栈:栈内存,主管安程序的运行,生命周期和线程同步;
线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题。
一旦线程结束,栈就Over!
栈:8大基本类型 + 对象引用 + 实例的方法
栈运行原理:栈帧( 父帧、子帧)
栈满了:StackOverflowError
栈+堆+方法区:交互关系
三种JVM
1.Sun公司 HotSpot™ 64-bit Server VM(目前我们所用的就是这个版本)
2.BEA JRockit
3.IMB J9 VM
堆
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量,变量~保存我们所有引用类型的真实对象;
堆内存中分为三个区域:
新生区(伊甸园区) Young/New
养老区 old
永久区 Perm
GC垃圾回收,主要是在伊甸园区和养老区~
假设内存满了,OOM,堆内存不够! Java.lang.OutOfMemoryError:java heap space,在JDK8以后,永久存储区改了个名字(元空间)
新生区
1.类:诞生和成长的地方,甚至死亡!
2.伊甸园,所有的对象都是在伊甸园区new出来的!
3.幸存者区(0,1)
真理:经过研究,99%的对象都是临时对象!
养老区
永久区(元空间)
这个区域常驻内存。用来存放JDK自身携带的Class对象。Interfacce元数据,存储的是Java运行时的一些环境或类信息~,这个区域不存在垃圾回收!关闭VM虚拟机就回释放这个区域的内存~
一个启动类,加载类大量的第三方Jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载。知道内存满,就会出现OOM;
jdk1.6之前:永久代,常量用来存放JDK池是在方法区;
jdk1.7 : 永久代,但是慢慢的退化了,'去永久代' ,常量池在堆中
jdk1.8之后:无永久代,常量池在元空间
//出现OOM怎么办呢?
1.尝试扩大堆内存看结果,如果还出现问题,肯定是代码出现问题了。
2.分析内存,看一下哪个地方出现了问题,调节JVM内存大小并打印GC的详细信息:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
元空间:逻辑上存在,物理上不存在
一个项目中,突然出现了OOM故障,那么该如何排除~
能够看到代码第几行出错:内存快照分析工具,Eclipse 的MAT,Jprofiler
Dubug,一行行分析代码!
MAT,Jprofiler作用
1.分析Dump内存文件,快速定位内存泄漏;
2.获得堆中的数据
3.获得大的对象
//设置内存调优参数
// -Xms 设置初始化内存分配大小,默认为物理内存的 1/64
// -Xmx 设置最大分配内存,默认为物理内存的 1/4
//-XX:+PrintDCDetails //打印GC垃圾回收信息
// -XX:+HeadDumpOnOutofMemoryError //oom DUMP
//-Xms1m -Xmx8m -XX:HeapDumpOnOutMemoryError
GC:垃圾回收机制
JVM在进行GC时,并不是对这三个区域统一回收,大部分时候,回收都是新生代~
1.新生代 2.幸存区(from,to) 3.老年区
GC两种类:轻GC(普通的GC),重GC(全局GC)
题目:
1.JVM的内存模型和分区~ 详细到每个区放什么?
2.堆里面的分区有哪些? Eden,form,to,老年区,说说他们的特点!
3.GC的算法有哪些? 标记清除法,标记压缩,复制算法,引用计数器,怎么用的?
4.轻GC和重GC分别在什么时候发生?
GC常用算法
1.引用计数器(引用则+1,用完则-1)
2.复制算法(Copying):每次GC都会把EDen活的对象一道幸存区中:一旦Eden区被GC后,就会是空的!
谁空谁是to(from,to)
当一个对象经理了15次GC,都还没有销毁, -XX: -XX:MaxTenuringThreshold=15(默认)
通过这个参数可以设定进入老年代的时间
优点:没有内存的碎片
缺点:浪费了内存空间~ 多了一半空间永远是空to,假设对象100%存活
复制算法最爱使用场景:对象存活度较低的时候:新生区~
3.标记清除算法(Mark and Sweep):
扫描这些对象:对或者对象进行标记
清除:对没有标记的对象,进行清除
优点:不需要额外的空间!
缺点:两次扫描,严重浪费时间,会产生内存碎片。
4.可达性分析算法
通过判断对象的引用链是否可达来决定对象是否可以被回收。
总结
内存效率:复制算法>标记清除算法>标记压缩算法
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
思考一个问题:难道没有最有算法吗?
答案:没有,没有最好的算法,只有最合适的算法----》 GC:最合适就是利用分代收集算法
年轻代:存活率低,复制算法!
老年代:
区域大:存活率
标记清除(内存碎片不是太多)+标记压缩混合实现