JVM探究
1、JVM的位置
2、JVM的体系结构
3、类加载器
作用:加载Class文件。
new Student();使用new关键字之后就会得到一个具体的实例,它的引用在栈里边,具体的人在堆里边。
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
栈中存的是car1,car2,car3的名字,真正的值是存放在堆中的,car1(name=“1“,age=1),car2(name=“2“,age=2),car3(name=“3“,age=3),栈中存的名字和堆中存的具体数值是有内存地址引用的。
这三个对象都是一个类加载器模板,但却是三个不同的对象。
-
虚拟机自带的加载器
-
启动类(根)加载器
-
扩展类加载器
-
应用程序加载器
4、双亲委派机制
为了保证安全。
按照层级查找
-
类加载器收到类加载的请求 Application
-
将这个请求向上委托给父类的加载器去完成,一直向上委托,直到启动类加载器
-
启动加载器检查是都能够加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子加载器加载
-
重复步骤3(启动加载器检查是都能够加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子加载器加载)
跟加载器(jre根目录中的rt.jar)>扩展加载器(jre目录中lib目录中)>自定义加载器(应用程序中)
-
APP-->EXC-->BOOT(最终执行)
-
如果BOOT中没有会去EXC中找
-
如果EXC中没有会去APP中找
5、沙箱安全机制
6、Native
public class Demo {
public static void main(String[] args) {
new Thread(()->{
},"测试线程名称").start();
}
//凡是带了native关键字的,说明Java的作用范围达不到了,去调用底层C语言的库
//会进入本地方法栈
//调用本地方法本地接口,JNI:Java native Interface
//JNI作用:扩展Java的使用,融合不同 的编程语言为Java所用
//Java诞生的时候C、C++横行,要想立足,必须要有调用C、C++的程序
//它在内存区域中专门开辟了一块标记区域:Native Method Stack,登记Native方法
//在最终执行的时候,加载本地方法库中的方法通过JNI
private native void hello();
}
7、PC寄存器
8、方法区
static,final,Class,常量池
9、栈
数据结构
栈:先进后出、后进先出:桶
队列:先进先出(FIFO:First Input First Output)
喝多了吐就是栈,吃多了拉就是队列
为什么main方法先执行,最后结束
栈溢出
栈:栈内存,主管程序的运行,生命周期和线程同步;
线程结束,栈内存也就释放,对于栈类说,不存在垃圾回收问题
一旦线程结束,栈就Over!
栈中存储的东西:8大基本类型+对象的引用+实例的方法
栈运行原理:栈帧
栈买了就会抛出错误:StackOverFlowError
栈+堆+方法区:交互关系
10、三种JVm
-
Sun公司 HotSpot
-
BEA Oracle JRockit
-
IBM J9 VM
11、堆
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把类实例,方法,常量,变量放入堆中。保存所有引用类型的真实对象。
堆内存中还要细分为三个区域:
-
新生区(伊甸园区)Young/New
-
养老区 Old
-
永久区 Perm
GC垃圾回收,主要是在伊甸园区和养老区。
假设内存满了,就会报错,OOM,堆内存不够。java.lang.OutOfMemoryError:Java heap space
在JDK8以后,永久存储区改了个名字(元空间);
-
新生区
-
类:诞生和成长的地方甚至死亡;
-
伊甸园,多有的对象都是在伊甸园区new出来的。
-
幸存区(0,1)
-
-
老年区
真理:经过研究,99%的对象都是临时对象!
-
永久区
这个区域常驻内存的。用来存放JDK自身携带的Class对象,Interface元数据,存储的是Java运行时的环境或类信息,这个区域不存在垃圾回收。关闭虚拟机就会释放这个区域的内存。
一个启动类,加载了大量的第三方jar包;tomcat部署了太多的作用;大量动态生成的反射类,不断的被加载,知道内存满,就会出现OOM
-
jdk1.6之前:永久代,常量池是在方法区
-
jdk1.7 :永久代,慢慢的退化了,去永久代,常量池在堆中
-
jdk1.8之后:无永久代,常量池在元空间
-
元空间:逻辑上存在,物理上不存在
public class Test {
public static void main(String[] args) {
//获取试图使用的最大内存
long maxMemory = Runtime.getRuntime().maxMemory();
//获取初始化的总内存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("最大内存:"+maxMemory+"字节\t"+maxMemory/(double)(1024*1024)+"M");
System.out.println("总内存:"+totalMemory+"字节\t"+totalMemory/(double)(1024*1024)+"M");
}
/***
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
-Xms为总内存
-Xmx为最大内存
遇到OOM:
1、尝试扩大堆内存看结果
2、分析内存,看一下哪个地方出现了问题(工具)
*/
}
项目突然出现OOM故障,应该如何排错:
-
能够看到代码第几行出错:内存快照分析工具,(eclipse插件)MAT,Jprofiler
-
Debug,一行一行的分析
MAT,Jprofiler作用:
-
分析Dump内存文件,快速定位内存泄露
-
获得堆中的数据
-
获得大的对象
-
.....
Jprofiler官网https://jprofiler.en.softonic.com/
Idea中下载Jprofiler插件
12、GC
GC分为两种:
轻GC:在Eden区
重GC(Full GC):在养老区
-
引用计数法
-
复制算法
好处:没有内存碎片
坏处:浪费内存空间,有一半幸存区永远是空的to区。
复制算法最佳使用场景:对象存活度较低的时候。
-
标记清除算法
优点:不需要额外的空间,相对复制算法的to区。
缺点:两次扫描严重浪费时间,会产生内存碎片。
-
标记清除压缩
总结
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
13、JMM
标签:Java,标记,算法,内存,JVM,new,加载 From: https://www.cnblogs.com/wx-36/p/16914591.html