JVM探究
1.JVM的位置
2.JVM的体系结构
JVM调优主要对 方法区和堆 做优化 Java栈 ,本地方法栈 ,程序计数器不会有垃圾
3.类加载器
作用: 加载class文件
1.虚拟机自带的加载器
2.启动类(根)加载器
3.扩展类加载器
4.应用程序加载器
4.双亲委派机制
app --》 exc – 》 boot
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
// -----??-----
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 首先,检查是否已经被类加载器加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 存在父加载器,递归的交由父加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 直到最上面的Bootstrap类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
5.沙箱安全机制
本地代码访问本地系统是授信的,远程代码访问本地系统是受限的,需要进行认证。比如安装软件的时候,需要管理员点击是否信任此程序。
6.Native
=Native Method :java调用非java代码的接口=
会进入本地方法栈
调用本地方法本地接口 JNI (java native interface : 扩展java使用 融合不同的变成语言为java所有!C,C++)
在内存区域中专门开辟一块标记区域: Native Method Stack 等级 native 方法
在最终执行的时候 加载本地方法库中的方法
应用:(java程序驱动打印机 管理系统 rebot类操作鼠标等)
7.PC寄存器
8.方法区
方法区是被所有的线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,就是所有定义的方法的信息都保存在该区域,此区域属于共享空间。
=静态变量,常量,类信息(构造方法,接口),运行时的常量池存在方法区中,但是实例变量存在堆内存中,与方法区无关。=
static final Class 常量池
(jdk1.8已经将方法区去掉了,将方法区移动到直接内存)
JDK1.8为什么要移除方法区
1)永久代来存储类信息、常量、静态变量等数据不是个好主意, 很容易遇到内存溢出的问题.JDK8的实现中将类的元数据放入 native memory, 将字符串池和类的静态变量放入java堆中. 可以使用MaxMetaspaceSize对元数据区大小进行调整;
2)对永久代进行调优是很困难的,同时将元空间与堆的垃圾回收进行了隔离,避免永久代引发的Full GC和OOM等问题;
9.栈
栈内存,主管程序的运行,生命周期和线程同步。
线程结束,栈内存也就释放了,对应栈来说 =不存在垃圾回收问题=。
存放8大基本类型+对象引用+实例的方法
栈运行原理:栈帧
10.三种JVM
sun hotpot
bea jRcokit
ibm j9vm
11.堆
heap:1个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后 一般会吧什么东西放到堆中? 类,方法,常量,保存所有引用类型的真实对象
堆内存中细分三个区域:
- 新生区(伊甸园区 Eden space)
- 养老区
- 永久存储区 (jdk8以后叫 元空间)
GC垃圾回收主要针对 伊甸园区和养老区
12.新生区,老年区
新生区:
- 类: 诞生和成长的地方 甚至死亡
- 伊甸园区
- 幸存者区(0,1)
13.永久区
这个区域常驻内存。用来存放JDK自身携带的Class对象。Interface元数据,存储的是java运行时的一些环境或类信息,该区域不存在垃圾回收!(关闭VM虚拟机就会释放该区域内存)
- jdk1.6 之前:永久代,常量池是在方法区
- jdk1.7: 永久代,常量池是在堆中
- jdk1.8之后 无永久化 常量池在元空间
元空间: 逻辑上存在,物理上不存在!
MAT,jprofiler
OOM:
- 尝试扩大堆内存看结果
- 分析内存
14.堆内存调优
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
-Xms:设置初始化内存分配大小 默认 1/64
-Xmx:设置最大分配内存 默认:1/4
-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError
。。。。。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k: 设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内 存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
idea安装插件jprofiler 和 windows客户端 重启后会出现该标志
HeapDumpOnOutOfMemoryError 后会产生dump文件
15.GC
JVM在进行GC时,并不是堆这三个区域统一回收,大部分时候,回收都是新生代
GC两种类:
轻GC(普通gc),重GC(全局GC)
GC题目:
- JVM的内存模型和分区-详细到每个区放什么?
- 堆里面的分区有哪些?Eden,from,to 老年区 说说他们的特点!
- GC的算法有哪些?标记清除法,标记压缩,复制算法,引用计数法,怎么用?
- 轻GC和重GC分别在什么时候发生?
常用算法
引用计数法:给每个对象计数 没用过的清除(一般不使用)
复制算法:
好处:没有内存的碎片
坏处:浪费了内存空间 多了一般空间永远是空to。
复制算法最佳使用场景:对象存活度较低的时候。
标记清除算法:
缺点: 2次扫描 浪费时间 会产生内存碎片。
优点:不需要额外的空间!
标记压缩
总结
内存效率: 复制算法》标记清除算法》标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法》标记清除算法
内存利用率:标记压缩算法=标记清除算法》复制算法
GC: 分代收集算法
年轻代: 存活率低 复制算法
老年代:区域大,存活率高 标记清除+标记压缩混合实现
JMM java menory model
是一种虚拟机规范。
用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。
单点登入 SSO