首先我们得知道jvm的内存结构和内存模型(JMM)这2个概念:
- 内存结构:存储数据的方式(物理上,逻辑上)
- 内存模型:定义了一系列规则来保证多线程环境下对数据的读写一致性(如:synchronized,volatile关键字)
内存结构
从图中可看出jvm内存主要分为:
- 程序计数器 :记住下一条jvm指令的执行地址(每个线程私有的)
- 虚拟机栈:每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈,某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的栈帧称为当前帧
jvm参数设置:Xss 是指设定每个线程的堆栈大小
当前方法的参数和局部变量都存放在该方法对应的栈帧中 - 本地方法栈:调用本地方法(native修饰的方法)
- 堆:用于存放对象(new出来的对象)
1. jvm参数设置: -Xms :初始堆大小 ; -Xmx :最大堆大小
2. 它是线程共享的,堆中对象都需要考虑线程安全的问题
3. 有垃圾回收机制
堆在逻辑上划分为“新生代”和“老年代”。由于JAVA中的对象大部分是朝生夕灭,还有一小部分能够长期的驻留在内存中,为了对这两种对象进行最有效的回收,将堆划分为新生代和老年代,并且执行不同的回收策略。
- 方法区(JDK1.8之前叫永久代,JDK1.8之后叫元空间)
存储加载的类字节码、class/method/field等元数据对象、static-final常量、static变量、jit编译器编译后的代码等数据,。另外,方法区包含了一个特殊的区域“运行时常量池”。常量池用于存放在字节码中使用到的所有字面量和符号引用(如字符串字面量),在类加载时,它们进入方法区的运行时常量池存放。
从图中可以看到串池StringTable在1.6是放在永久代(方法区)的常量池中,而在1.8中串池放到了堆中,提高对串池中数据的垃圾回收频率。
垃圾回收机制
判断对象是否可回收的方法有2种:
- 引用计数法:对象被其他对象引用一次,则计数加一,不被引用,计数减一,为0时则该对象可以被垃圾回收,但是这会出现一个问题,如果2个对象相互引用则这2个对象永远也不会被回收。
- 可达性分析算法:Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象。扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以回收。
对对象垃圾回收的方法有3种:
- 标记清除:不被gcroot所引用的对象直接进行垃圾回收,但是会导致零散空间
- 标记整理:垃圾回收后对零散空间进行整理
- 复制算法:将要占用双倍内存空间,from空间被垃圾回收后将剩余的还未被回收的对象复制到to空间中,依次进行下去,只要发生垃圾回收就进行复制操作。
jvm采用分代垃圾回收策略:
- 对象首先分配在伊甸园区域
- 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的
对象年龄加 1并且交换 from to - minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
- 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
- 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时
间更长
参考资料:
https://www.php.cn/java-article-410259.html
https://www.cnblogs.com/ceshi2016/p/8447989.html
https://xuexi.boxuegu.com/video.html?courseId=1551