JVM

JVM结构

java文件,通过javac变成Class.File,加入类加载器Class Loader,与下图交互。

JVM

方法区 Method Area

堆 Heap

栈 Stack

本地方法栈 Native Method Stack

栈里不可能存在垃圾(放的是一个指向堆的地址)

垃圾只存在堆中,99%的调优都是调整方法区和堆

类加载器

收到.class文件,进入JVM的Class Loader,加载初始化为CLass的模板,以后声明这个类都是引用这个模板实例成对象。

​ |JVM

​ |

XX.class---|--->Class Loader----(加载,初始化)--->XX Class---(new)--->XX的实例

​ | ^ | ^ |

​ | |---(getClassLoader()方法)---| |---(getClass(方法))---|

1.虚拟机自带加载器

2.启动类(根)加载器

3.扩展类加载器

4.应用程序加载器

双亲委派机制

当前写程序的位置应用程序加载器:APP

上一级为扩展类加载器:EXC

最上级为根加载器:BOOT

运行一个程序会不断向上查找class

如果该class在BOOT层有,则执行BOOT层class。

如果不在BOOT层,在EXC层找到,则执行EXC层class。

如果前面都没有找到,在APP层找到,则执行APP层class。

如果都找不到,报错Class Not Found。

1.类加载器收到类加载请求

2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器

3.启动加载器检查是否能加载当前这个类,能加载就结束,使用当前加载器,否则,抛出异常通知子加载器进行加载

4.重复步骤3,如果最后仍未找到,报错Class Not Found

沙箱安全机制

沙箱机制就是将java代码限定在虚拟机JVM特定的运行访问中

沙箱主要限制系统资源访问

沙箱组成

字节码校检器

类装载器(存取构造器、安全管理器、安全软件包)使用双亲委派机制

native关键字

说明java语言达不到了,需要调用底层C语言的库

会进入本地方法栈

调用本地方法本地接口 JNI

JNI作用:扩展java的使用融合不同的编程语言为java所用(最初C,C++)

在内存区域中专门开辟了一块标记区域:Native Method Stack

在最终执行的时候,加载本地方法库中的方法通过JNI

PC寄存器

程序计数器

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

方法区

Method Area 方法区

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;

==静态变量、常量。类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关==

static final Class 常量池

栈:先进后出,后进先出

队列:先进先出

三种JVM

Sun公司 HotSpot (使用最多)

BEA公司 JRockit

IBM公司 J9 VM

堆Heap

一个JVM只有一个堆内存,堆内存的大小是可以调节的。

类加载器读取了类文件后,会把类、方法、常量、变量放在堆中,保存所有对象引用的实例。

堆内存中细分三个区域

新生区:伊甸园(Eden Space),幸存区0区(from),幸存区1区(to)

幸存区0区和幸存区1区一直发生动态交换

养老区:养老区(Old)

GC垃圾回收,主要在伊甸园区和养老区

真理:经过研究99%的对象都是临时对象!

永久区:永久存储区(Perm)

这个区域常驻内存的。用了存放JDK自身携带的Class对象。Interface元数据,存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存。

一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载,直到内存满,就会出现OOM(堆内存满了)。

jdk1.6之前:永久代,常量池是在方法区

jdk1.7:永久代,但是慢慢退化了,去永久代,常量池在堆中

jdk1.8:无永久代,常量池在元空间

出现OOM故障

1.尝试扩大内存看结果

2.分析内存看看哪出了问题

内存快照分析工具MAT,JPofiler

Dubug,一行行分析代码

MAT,JPofiler工具的作用:分析Dump内存文件,快速定位内存泄露;获得堆中的数据;获得大的对象……

GC

GC两种类:轻GC(普通GC,针对新生区和幸存区),重GC(全局GC)

堆里的分区(Eden,from,to,老年区)

GC算法

标记清除法

扫描对象,对活着的对象标记

扫描对象,对没有标记的对象清除

优点:不需要额外的空间

缺点:两次扫描,严重浪费时间,会产生内存碎片

标记压缩

对标记清除优化**

扫描对象,对活着的对象标记

扫描对象,对没有标记的对象清除

再次扫描,压缩,防止内存碎片产生,把存活的对象移动

引用计数法

计算被使用的次数,为0的清除,会占用一部分性能,JVM已经不怎么用这个算法了(很low)

复制算法

对幸存区操作,把用的对象放在from区,清空伊甸园区和to区,超过一定引用次数(MaxTenuringThreshold设置进入养老区的次数)会放在养老区

优点:没有内存碎片

缺点:浪费内存空间,多了一半空间永远是空to

最佳使用场景:对象存活度低,所以一般放在新生区用这个算法

总结

内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)

内存整齐度:复杂算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

年轻代:存活率低,复制算法

老年代:区域大,存活率,标记清除+标记压缩混合

GC调优,就是调节年轻代和老年代的算法

JMM:Java Memory Model

java内存模型

作用:缓存一致性协议,用于定义数据读写规则

JMM定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)

解决共享内存可见性这个问题:volilate

JMM:抽象的概念,理论

等等……