动易学校网站管理系统 漏洞,重庆手机网站推广资料,程序代码大全,网上销售型的企业网站GC算法
一、对象存活判断
在正式进入GC算法之前先要了解JVM是怎么在GC过程判断对象存活的。
常见的判断方法有以下两种
1. 引用计数法
简而言之就是给 Java 对象添加一个引用计数器#xff0c;每当有一个地方引用它时#xff0c;计数器 1#xff1b;引用失效则 -1。当G…GC算法
一、对象存活判断
在正式进入GC算法之前先要了解JVM是怎么在GC过程判断对象存活的。
常见的判断方法有以下两种
1. 引用计数法
简而言之就是给 Java 对象添加一个引用计数器每当有一个地方引用它时计数器 1引用失效则 -1。当GC发生时把引用为0的全部清理掉
比如
A a new A();
B b new B();
a.b b;对象a引用了对象b,a的计数器为0b的计数器为1当GC发生时a被回收b的计数器-1变成0下一次GC发生时b就会被回收。
这个方法虽然简单粗暴但是有一个致命性的问题那就是无法判断循环引用
A a new A();
B b new B();
a.b b;
b.a a;比如遇到这种情况即使a和b没有被其他对象引用也无法被回收。
2. 可达性分析
将一系列的 GC Roots 对象作为起点从这些起点开始向下搜索所有在引用链中的对象即为存活对象。 可作为 GC Root 的对象有 Java虚拟机栈栈帧的本地变量表中引用的对象本地方法栈 中 JNI引用对象方法区 中常量、类静态属性引用的对象 二、GC算法
1. 复制算法
复制算法将可用内存按容量划分为大小相等的两块每次只使用其中的一块。当这一块的内存用完了就将还存活着的对象复制到另外一块上面然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收内存分配时也就不用考虑内存碎片等复杂情况只要移动堆顶指针按顺序分配内存即可实现简单运行高效。
复制算法的问题有两个
空间浪费内存被分为2部分效率不稳定当存活对象过多的时候效率会非常低
2. 标记-清除算法
顾名思义这种方法分为两个步骤标记和清除
标记步骤GC开始的时候JVM先从GC Roots开始遍历整个堆将存活对象进行标记清除步骤将未被标记的所有对象全部清理掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4lOKN0p-1691029166510)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95.png)]
缺点造成大量的内存碎片
3. 标记—整理算法
标记-整理是对标记-清除的改进同样分为两个步骤只是将清除换成了整理
整理步骤将被标记的对象全部移动到内存一端然后将存活对象的后面内存全部释放
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyT1Kw7a-1691029166511)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/%E6%A0%87%E8%AE%B0-%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95.png)]
4.分代收集
分代收集与其说是一种算法不如说是解决方案。
分代收集将堆内存分为新生代和老年代针对新生代和老年代的特性采用不同的GC算法进行回收。
新生代创建和消亡频繁存活率低因此采用复制算法
老年代存活率高采用标记-清除或者标记-整理
当新生代的对象经过多次GC之后依然存活就会被复制到老年代
三、JVM的做法
JVM采用分代收集的方式进行GC将堆内存分为了新生代、老年代和持久代并且将新生代分为Eden、From Survivor、To Survivor三个区间
1. 内存区划分
新生代: Eden From Survivor(S0) To Survivor(S1)老年代持久代方法、static变量、JAVA类1.8之后变为元空间作用于本地内存被所有JVM实例共享
划分Survivor区的好处是防止老年代区被过快填满而发生Full GC这个过程将会非常慢如果没有Survivor做缓冲区每次新生代发生GC都会转化为老年代老年代很快就满了。
而将Survivor区分为两个区间是为了减少内存碎片当发生GC的时候会将Eden和From Survivor的所有存活对象复制到To Survivor清空Eden和From Survivor然后将From Survivor和To Survivor角色替换这也是两个Survivor区大小一样的原因。
2. 内存区的比例
新生代:老年代 1:2, 可通过参数-XX:NewRatio配置Eden : S0 : S1 8 : 1 : 1, 可通过参数-XX:SurvivorRatio配置新生代转化为老年代的复制次数为15可以通过参数-XX:MaxTenuringThreshold配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kD1qxk1A-1691029166511)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/JVM%E5%A0%86%E5%86%85%E5%AD%98%E5%88%86%E4%BB%A3.jpg)]
JVM内存结构
根据《Java虚拟机规范》JVM的内存数据结构分为以下几个部分 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YdD15XEL-1691029166512)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.jpg)]
不过我们都知道JVM的实际制定不一定要完全按照规范在JDK7以前HotSpot虚拟机都是以“永久代”的形式实现方法区JDK7的时候将运行时常量池包含类信息、字符串常量池等移动到了堆内存中而在JDK8使用了“元空间”完全代替了老年代但是元空间是放在本地内存中由多个JVM实例锁共享的所以这和《Java虚拟机规范》中方法区在JVM实例内部已经不符合了。
1. 方法区
方法区存储了类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。 在Hotspot虚拟机中对于该区域的实现发生了多次的变化。
JDK6以前使用永久代实现方法区JDK7依然使用永久代保存类型信息但是常量池和静态变量移动到了堆内存JDK8: 使用元空间实现方法区且元空间存在于本地内存多个JVM共享这意味着常量池可以被多个JVM共享减少了内存的消耗
所以JAVA8中的实际内存结构应该是这样的 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-psyNWBuZ-1691029166512)(https://joshua-1301529810.cos.ap-chengdu.myqcloud.com/img/Java8%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.jpg)]
2. 堆
这个很熟悉了堆是整个JVM管理的内存中最大的一块它的唯一目的就是存储对象Java对象几乎全部都存放在对上《Java虚拟机规范》中提到所有的对象和数组都应当在对上分配其实数组也是一个特殊的对象只不过是由虚拟机操作字节码生成的格式为[Lxxx.xxx.xxx,之所以说是几乎是因为现在Java确实存在这种手段。
在GC算法的笔记中中也提到过HotSpot采用分代收集做GC收集所以JVM堆从逻辑上分为了新生代和老年代默认比例12其中新生代由由Eden和2个Survivor区组成默认比例811。之所以说是“逻辑上”是因为从G1收集器开始已经出现了不使用分代进行GC的做法在这种情况下JVM仅保留了逻辑概念上的分代以便于使用分代回收的收集器依然能够工作。以G1收集器来看它就是将整个堆分为若干个小块每次进行GC的时候会根据哪块内存中存放的垃圾最多将存活的对象复制到新的块中然后释放掉这一块内存所以在G1收集器下新生代和老年代都是一部分内存块的集合罢了。
3. 虚拟机栈
每个线程都会拥有其对应的栈其生命周期是和线程一致的。 虚拟机栈描述的是Java方法执行的线程内存模型每个方法被执行的时候JAVA虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口德国信息。每一个方法被调用直至执行完毕的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 4. 本地方法栈
本地方法栈与虚拟机栈功能很相近区别就是本地方法栈是为Native方法服务《Java虚拟机规范》中没有对本地方法栈做任何规定甚至于连我们的HotSpot就直接将本地方法栈和虚拟机栈合二为一了。
5. 程序计数器
这个放在最后主要是因为这部分对于开发者没有直接关系它只和JVM执行JAVA字节码有关系。我们可以将程序计数器比作字节码的行号指示器它记录了当前线程执行的是那一条字节码指令从而可以让线程在切换后恢复到正确的执行位置。
如果线程正在执行的是Java方法这个计数器记录的就是字节码指令的地址如果执行的是native方法这个计数器值就该为Undefined程序计数器也是《Java虚拟机规范》中唯一一个没有规定OOM的区域。
JVM参数
1.常用JVM参数
-Xms:初始堆大小-Xmx:最大堆大小-Xmn:新生代大小-XX:NewRatio:设置新生代和老年代的比值。如为3表示年轻代与老年代比值为13-XX:SurvivorRatio:新生代中Eden区与两个Survivor区的比值数值为Eden区占比默认为8。注意Survivor区有两个。如为3表示EdenSurvivor1: Survivor2 3:1:1一个Eden区占整个新生代的1/5-XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0则跳过Survivor直接进入老年代-XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小Java8以前-XX:MetaspaceSize、-XX:MaxMetaspaceSize:分别设置元空间最小大小与最大大小Java8以后
2.收集器设置
-XX:UseSerialGC:使用SerialSerialOld-XX:UseParallelGC:使用ParallelScavengeSerialOld-XX:UseParalledlOldGC:使用ParallelScavengeParallelOld (JDK8默认组合)-XX:UseConcMarkSweepGC:使用ParNew(CMS SerialOld)-XX:UseG1GC:使用G1 JDK9之后默认