一、JVM体系结构
HotSpot
参考Java 常量池详解(一)字符串常量池_new hilbert()的博客-优快云博客_java常量池和字符串常量池
二、类加载器 ClassLoader
bootstrap classloader(c++) ->
加载 %JAVA_HOME%/lib
目录下的 jar 包和类或者被 -Xbootclasspath
参数指定的路径中的所有类。
extension classloader ->
加载 %JRE_HOME%/lib/ext
目录下的 jar 包和类,或被 java.ext.dirs
系统变量所指定的路径下的 jar 包。
application classloader ->
加载当前应用 classpath 下的所有 jar 包和类。
user classloader
双亲委派机制:向上检查是否加载过,向下进行加载。
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,则向下寻找可以加载的加载器。
沙箱安全机制:外部代码在权限低的区域运行。
加载过程:加载-->连接(验证-->准备-->解析)-->初始化。
三、方法区 Method Area
static 、final 、Class模板 、常量池
四、栈 Stack
线程级别,线程结束,栈内存释放,不存在垃圾回收问题。
存放内容:8种基本数据类型、对象引用、形参实参等局部变量。
一个对象实例化的过程:
参考Java对象的实例化过程 - HelloJava菜鸟社区
五、堆 Heap
一个jvm只有一个堆内存,大小可调节。
默认情况下,分配的总内存为电脑内存的1/4,初始化内存为1/16。
可通过参数设置:-Xms1024m(初始化内存) -Xmx1024m(分配总内存)。
保存new出来的实例化对象。
分为三个区域:
1.新生区(伊甸园区 Eden) Young/New
(伊甸园区+幸存0区+幸存1区)
伊甸园区是对象诞生的地方,伊甸园区满了会进行轻GC,部分死亡,剩下的进入幸存0区、幸存1区。
2.养老区 Old
伊甸园区+幸存0区+幸存1区都满了会进行重GC,幸存下来的进入养老区。
通过参数 -XX:MaxTenuringThreshold=15 ,设置经过15次GC还存活后进入老年区。
新生区、养老区都满了会OOM(OutOfMemory)。
PS:一般99%的对象都是临时对象!
3.永久区 Perm(持久代)
常驻内存,存放JDK自带的Class对象,Interface元数据,Java运行时的一些环境或类信息,不存在垃圾回收,关闭JVM后释放内存。逻辑上属于堆,但实际上不在堆中,非堆。
如果一个启动类加载了大量的第三方Jar包,Tomcat部署了大量的应用,大量动态生成的反射类,这些不断被加载可能会出现OOM。
JDK1.6之前:永久代,常量池在方法区;
JDK1.7 :永久代,慢慢退化,常量池在堆中;
JDK1.8之后:元空间,常量池在元空间,不占用堆内存,只和本地内存有关。
PS:GC立即回收主要在伊甸园区和养老区。
JDK8以后永久区改为元空间。
六、垃圾回收 GC
回收哪些?
引用计数法:判断哪些对象引用数为0,进行GC。(弊端 对象间循环引用)
可达性分析:判断哪些对象没有到GC Roots的引用链。
引用类型:
强引用 即使OOM也不回收
软引用 内存不够时回收
弱引用 GC时扫描到就回收
虚引用 主要用来跟踪对象被垃圾回收的活动,和引用队列(ReferenceQueue)联合使用
GC四大算法:
1.复制算法
在新生区,伊甸园区幸存下来的进入to区(幸存1区),from区(幸存0区)中的复制到to区,然后to区变为from区,原from区变为to区(谁空谁是to)。
-XX:MaxTenuringThreshold=15 15次GC后存活的进入老年区。
优点:没有内存碎片
缺点:浪费内存空间(to区)
最佳使用场景:对象存活度较低;新生区。
2.标记清除算法(CMS收集器 并行)
优点:不需要额外空间;
缺点:两次扫描,浪费时间,会产生内存碎片。
3.标记整理(压缩)算法
4.分代收集算法(最合适)(G1收集器)
新生代:存活率低->复制算法
老年代:区域大、存活率高->标记清除(碎片不多时)+标记压缩(碎片过多时)
对比:
时间效率:复制>标记清除>标记压缩
内存整齐度:复制=标记压缩>标记清除
内存利用率:标记压缩=标记清除>复制
七、Java内存模型 JMM
volilate:关键字,保证共享对象可见性,线程在工作内存更新数据后主内存也要更新,只能用于修饰变量,性能比synchronized要好,多线程访问volatile不会发生阻塞,而sychronized会出现阻塞。
synchronized:关键字,保证可见性和原子性,可以修饰变量、方法以及代码块。
lock:接口,保证可见性和原子性。