ps如何做网站专题,怎么查网站找谁做的,空压机东莞网站建设,深圳外贸网站制作公司JVM规范说了并不需要必须回收方法区#xff0c;不具有普遍性#xff0c;永久代使用的是JVM之外的内存 引用计数:效率要比可达性分析要强#xff0c;随时发现#xff0c;随时回收#xff0c;实现简单#xff0c;但是可能存在内存泄漏 局部变量表#xff0c;静态引用变量不具有普遍性永久代使用的是JVM之外的内存 引用计数:效率要比可达性分析要强随时发现随时回收实现简单但是可能存在内存泄漏 局部变量表静态引用变量通过引用链关联的引用链是不会被回收局部变量表天然作为GCROOTS 1虚拟机栈中引用的对象栈帧中的本地方法表。
2方法区中1.8称为元空间的类静态属性引用的对象一般指被static修饰的对象加载类的时候就加载到内存中。
3方法区中的常量引用的对象。
4本地方法栈中的JNInative方法引用的对象。
注意即使可达性算法中不可达的对象也不是一定要马上被回收还有可能被抢救一下要真正宣告对象死亡需经过两个过程。
1.可达性分析后没有发现引用链
2.查看对象是否有finalize方法如果有重写且在方法内完成自救[比如再建立引用]还是可以抢救一下注意这边一个类的finalize只执行一次这就会出现一样的代码第一次自救成功第二次失败的情况。[如果类重写finalize且还没调用过会将这个对象放到一个叫做F-Queue的序列里这边finalize不承诺一定会执行这么做是因为如果里面死循环的话可能会时F-Queue队列处于等待严重会导致内存崩溃这是我们不希望看到的。就是只是进行新生代回收的时候老年代的引用也可以作为GCROOTS public class Test {public static Test obj;//这是一个类变量// Override
// protected void finalize() throws Throwable {
// System.out.println(调用当前链上的finalize方法);
// objthis;//当前带回收的对象在finalize方法上和一个引用链上面的对象建立了联系
// }public static void main(String[] args) throws InterruptedException {objnew Test();//对象第一次拯救自己objnull;System.gc();//调用垃圾回收器System.out.println(第一次GC);//因为finalizer线程优先级很低,暂停2s来等待他Thread.sleep(3000);if(objnull){System.out.println(对象已经死了);}else{System.out.println(对象还活着);}objnull;System.gc();//调用垃圾回收器System.out.println(第二次GC);//因为finalizer线程优先级很低,暂停2s来等待他Thread.sleep(3000);if(objnull){System.out.println(对象已经死了);}else{System.out.println(对象还活着);}}
}垃圾回收算法: 任何时候都可能当系统觉得你内存不足了就会开始回收常见的比如分配对象内存不足时这里的内存不足有可能 不是占用真的很高可能是内存足够但是没有连续内存空间去放这个对象当前堆内存占用超过阈值时手动 调用 System.gc() 建议开始GC时系统整体内存不足时等 标记是非垃圾的对象就是可达的对象然后清除清楚的是垃圾对象要先递归进行遍历所有可达对象然后清除的时候需要再开始遍历一遍还需要进行维护空闲列表 就比如说我们的硬盘只要你不小心点击了格式化此时也不是真正的进行格式化只是标记性删除但是千万不要再向里面存放数据因为数据会覆盖就不好恢复了 复制算法:内存利用率贼低 首先经过可达性分析在A区找到可达的对象一旦找到了可达的对象就不需要进行标记直接将可达的对象进行复制算法放到另一块区域B这是另一块空间的所有区域B的对象都是连续的 缺点:维护引用和对象的地址映射 回收的对象比较少存活的对象比较多那么移动的对象比较多但是还要大量维护指针和对象的关系老年代不适合使用复制算法因为很多对象都不死老年代复制对象开销太大 标记整理算法: 还要移动位置还要修改引用对象关系很麻烦这个算法比标记清除算法效率还低 分代回收: 新生代:老年代1:2edin区:幸存者1区:幸存者2区: 标记的开销和存活的对象成正比因为标记只能标记存活的对象 清除阶段要进行全堆空间线性的遍历 压缩或者是整理和存活对象的大小成正比 Stop The World: 先确定GCROOTS枚举根节点此时要进行Stop The World确保数据的一致性 stop the world停止的是用户线程就是为保证一致性 可达性分析算法中枚举根节点会导致所有Java执行线程停顿 衡量一个垃圾回收器的标准就是吞吐量和低延迟 增量收集算法: 比如说我现在有一个房子我一直不进行清理一直制造垃圾直到三个月之后才清理一次此时清理的时间就比较长阻隔用户线程的时间就比较长但是如果说隔一会清理一会效果就会比较好用户线程和回收线程协调交替执行看样子就是并发的执行从而到达一种低延迟的行为就是为了让用户感觉好一点 被STW中断的应用程序线程会在完成GC之后恢复频繁的中断会让用户感觉像是网速不快造成的电影卡顿一样 CMS自称低延迟开发中不要显示的进行GC导致STW 分区算法:降低停顿时间主要是保证低延迟而不是吞吐量 有的分区存放大对象有的区域存放小对象回收区域的个数取决于时间的长短可以控制可控时间 System.gc():提醒JVM垃圾回收器去执行GC但是不确实马上执行GC底层是调用RunTime().getTime().gc()进而也不能确定finlizle方法一定会被调用 Full GC 就是收集整个堆包括新生代老年代和方法区但是调用System.gc系统做了免责声明GC具体干不干不怪这个方法仅仅是提醒JAVA虚拟机进行垃圾回收但是实际上是否进行垃圾回收System.GC()不保证 做性能测试之前先进行GC局部变量表第一个位置存放的是this 不会触发垃圾回收 buffer作用域已经过了所以buffer肯定用不上了系统会判定buffer占用的slot为可覆盖的slot一但value覆盖buffer所在的槽buffer引用被覆盖此时没有任何引用指向字节对象数组所以此时触发System.gc就会回收垃圾 内存溢出和内存泄漏: 空间不够GC之后空间还不够才会报OOM 如果动态加载很多类intern方法调用太多都会造成OOM本地内存报OOM的情况相对来说比较少一些 1)宽泛意义上的内存泄漏:其实可以把对象定在方法内部作为局部变量当方法执行完成以后对象就要被回收了但是如果将这个变量成员变量那么这个对象的存活周期就变得很长但是如果是这个变量静态变量还是大对象类变量随着类的加载而加载随着类的消亡而消亡也会理解成宽泛的内存泄露还比如说Session会话不使用就没有必要存放内存泄漏可能会导致内存溢出如果出现很多生命周期很长的对象再加上很多没有办法回收的数据的存在就很有可能造成内存泄露 2)存在很多生命周期很长的对象而本身生命周期没有这么长的对象而又生命很长可以称之为是内存泄漏 严格意义宽泛意义上的内存泄漏有些对象不使用但是还存着引用链有可能忘记断开引用如右图对象没有用处但是还存在引用链可能造成内存回收ThreadLocal 1)单例模式中的对象是static单例对象的生命周期和应用程序是一样长的一个进程只有这一个实例就比如说RunTime实例声明的是静态的每一个进程只有一个实例会随着程序的执行而产生随着进程的结束而销毁如果此时单例对象关联了一个外部的很大的对象这个外部对象用一会就不用了单例对象的生命周期非常长但是这个引用关系又断不掉所以此时连带着外部对象的生命周期也很长本身又不用但是无法释放内存此时这个对象还无法回收引用链条得不到释放 2)当内部资源外部资源需要交互的时候是需要进行手动的关闭资源链接没有关闭资源GC就无法回收这些对象只有当程序结束的时候才能回收这些链接的对象此时可能就会发生内存泄漏 程序中的并行和并发(一个CPU快速切换CPU不是真正意义上的同时执行): 并行就是在具体某一个时刻的时候有三个线程同时的进行执行主要是取决于多核CPU而并发在某一个时间点上面只能有一个进程在执行在时间段内是可能有多个线程 垃圾回收器: 下面的绿线表示用户线程红色表示垃圾回收器串行垃圾回收器很慢 安全点: 点比较少这个时候GC等待时间太长用户线程执行时间过长还有可能会导致OOM 点太多STW时间也会变长这个时候切换线程开销很大 最好是在跳转的时候或者调用新方法的时候执行时间比较长比如说在进行方法调用的时候要将方法压入虚拟机栈把它们作为savepoint最好不要在程序指令执行很快的时候设置saveponintsleep和block引用关系也不会发生变化 线程处于睡眠或者是阻塞状态这个线程无法响应JVM中断请求此时你在让去走到中断安全点挂起是不可能的不可能唤醒吧 安全区的对象引用关系不会发生变化因此安全区域发生GC程序依然会继续执行 要出安全区域了但GC没有结束程序会等待安全区域应该是并发的但不能走出安全区域 枚举出整个“GC Roots”是非常麻烦的首先运行时数据本身就是动态的在这个枚举的过程中必须保证其原子性并且在今天 Java应用越来越大的情况下单单一个方法区就有可能数百上千兆里边的类、常量等等更是多如果要逐个找出这些 根节点实在是一个非常非常耗时的事情。 那 JVM 是如何解决的呢 首先在进行根节点选举时必须暂停全部的用户线程把这个过程称为“Stop The Word”下面简称STW但必须要说明STW不一定是全局的也可以是局部的这和安全点的类型有关。此时说的必须暂停全部用户线程只是因为GC时必须使全部线程进入安全点 在HotSpot的解决方案中是使用一组称为OopMap的数据结构来存放这些对象的引用OopMap在类加载动作完成时生成也就是说当用户线程暂停下来之后其实并不需要一个不漏的检查完所有的执行上下文和全局的引用位置而是直接通过OopMap来获取栈上或寄存器里哪里有GC管理的指针引用指针 安全点 OopMap解决了一部分问题但没有解决所有的问题试想一下对象中的引用关系并非一成不变如果每次执行一条字节码指令都去生成一个OopMap那就必须消耗大量额外的存储空间为了解决这个问题HotSpot并没有让每条指令都生成OopMap而是只在特定的位置生成OopMap这个位置就被称为安全点放置安全点的位置一般是以“是否具有让程序长时间执行特征”为标准进行选定 引用:前提是引用关系在的情况下都是可达的没有引用关系啥引用都会被回收 强引用:如果发生OOM也不会回收 软引用:内存不足回收内存充足就不会回收主要用于缓存当我们第一次进行深度优先遍历之后把那些不可达的对象垃圾回收之后发现空间还是不够于是就会尝试把可达的软引用所指向的对象进行回收也叫做二次回收如果回收了软引用空间还不够就会报OOM 但是强引用才会导致内存溢出软引用是不会导致内存溢出的 弱引用:只要进行GC就会进行垃圾回收主要用于缓存 虚引用:唯一目的就是对象回收的跟踪 软引用:内存足够不会回收可触及的也就是软引用可达的对象当堆内存不够的时候才会回收软引用是不会报内存溢出的Mybatis内部缓存就是用到软引用 此时如果说刚好能容得下大数组大对象还刚好容不下这个弱引用不一定说回收软引用就会发生OOM不一定报OOM之前才会回收 public static void main(String[] args) throws InterruptedException {//声明一个强引用,将强引用的对象放入到软引用的形参里面Object s1new Object();SoftReferenceObject referencenew SoftReference(s1);//创建软引用s1null;//但是此时s1所指向的对象既有强引用s1的关联,又存在着弱引用的reference的关联,所以此时应该把强引用的关联解除,也就是让s1里面不指向任何数据,保证这个对象只有一个软引用关联System.out.println(reference.get());//防止强引用来干扰} import java.lang.ref.SoftReference;
class User{public String username;public String password;public User(String username,String password){this.usernameusername;this.passwordpassword;}Overridepublic String toString() {return User{ username username \ , password password \ };}
}
public class Test {public static void main(String[] args) throws InterruptedException {//声明一个强引用,将强引用的对象放入到软引用的形参里面User usernew User(李四,12503487);SoftReferenceObject referencenew SoftReference(user);//创建软引用usernull;//但是此时s1所指向的对象既有强引用s1的关联,又存在着弱引用的reference的关联,所以此时应该把强引用的关联解除,也就是让s1里面不指向任何数据//上面的三行代码等价于 SoftReferenceObject referencenew SoftReference(new User(李四,12503487)//1.从软引用中可以获取到软引用对象的实例,可以获取到强引用的实例,由于堆空间内存足够,所以不会回收软引用的可达对象System.out.println(reference.get());//2.创建一个大对象try {int[] array new int[10000 * 100000];}catch (Throwable e){e.printStackTrace();}System.gc();System.out.println(reference.get());//在报OOM的时候,垃圾回收器会回收软引用的可达对象}
} 弱引用:非必需的对象 软引用不如弱引用回收的快因为软引用要使用算法判断内存是否不足 虚引用:一旦将弱引用回收就会将虚引用存放到引用队列中可以追踪垃圾回收过程虚引用的作用主要是将回收的对象放在队列中 进行GC对象追踪 import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class Test{public static Test test;//对当前对象的声明public static ReferenceQueueTest queuenull;//声明引用队列public static class WorkThread extends Thread{Overridepublic void run() {while (true) {if (queue ! null) {PhantomReferenceTest reference null;try {//如果这个对象被回收了,那么虚引用会被放到等待队列里面reference (PhantomReferenceTest) queue.remove();} catch (InterruptedException e) {e.printStackTrace();}if (reference ! null) {System.out.println(追踪垃圾回收过程:当前Test实例被GC了);}}}}public static void main(String[] args) {Thread tnew WorkThread();t.setDaemon(true);//设置为守护线程当程序中没有非守护线程时守护线程也就执行结束t.start();//1.创建虚引用队列queuenew ReferenceQueue();Test testnew Test();//2.创建Test 对象的虚引用PhantomReferenceTest refnew PhantomReference(test,queue);testnull;//3.尝试获取到虚引用中的对象获取失败,因为虚引用的对象不可以被获取到System.out.println(ref.get());System.gc();//4.执行GC之后,虚引用引用的对象会被回收,此时会把虚引用放入到引用队列里面}}
} 不同的引用类型主要是取决于不同对象的可达性状态和对象垃圾收集的影响强引用就是普通对象的一个引用只要有一个强引用指向一个对象就表示这个对象还活着垃圾回收器就永远无法回收这一类的对象只有没有其他引用关系或者是超过了引用的作用域或者是显示将引用设置为null才会进行垃圾回收 软引用:只有当JVM认为内存不足的时候才会进行试图回收引用所指向的对象软引用主要适用于实现内存敏感的缓存如果还有空闲内存就可以暂时去保留缓存当内存不足的时候会清理掉这样就可以保证使用缓存的同时不会耗尽内存 弱引用是相对于强引用关联的不管内存是否足够都会回收弱引用 虚引用不会决定对象的生命周期它提供了一种确保对象被finlize之后去做某些事情的一种机制当垃圾回收器准备去回收一个对象的时候如果发现她还存在虚引用就会在回收对象的内存之前就会把这个虚引用加入到与之关联的引用的队列里面那么程序可以通过判断引用队列是否已经加入了虚引用来去了解被引用的对象是否要进行垃圾回收然后就可以在引用对象被回收之前来采取必要的一个行动 终结器引用:finalReference 吞吐量a/ab这个数值越大越好垃圾收集开销b/ab这个值越小越好 暂停时间STW时间 收集频率:回收的频率低不代表一次GC的时间短大学洗衣服 一次赞一快洗(时间比较长)VS经常洗(一天一洗时间比较短)频率越高STW时间短一点 1)吞吐量:吞吐量越大越好就是用户线程所消耗的时间在整个JVM生命周期中所占用的时间越长那么垃圾频率就越低但是每一次执行GC那么用户线程停止STW时间就越长(类比于洗衣服)一次性的暂停时间就很长单位时间内用户线程做的事情更多 2)低延迟:注重每一次的暂停时间变短用户线程暂停时间短那么垃圾回收GC的频率就越高因为暂停时间短每一次GC都收集不了多少垃圾线程频繁切换也需要时间每一次本来就注重低延迟要求GC垃圾回收短况且线程上下文切换还消耗时间每一次GC垃圾又回收不了多少那么最终一共的STW时间肯定会比吞吐量的STW时间长(类比于洗衣服) 3)高吞吐量和低延迟是矛盾的 这就类似于洗衣服从宿舍去水房的时间和从水房回到宿舍的时间就类似于线程切换 和用户交互的程序延迟要短一些争取在垃圾回收的过程中多线程回收 有的是服务器端吞吐量要高一些 G1垃圾回收器就是可以保证在给定停顿时间的基础上尽量的提高吞吐量 JDK7之前实线Serial OLD GC是CMS的后备方案 在JDK9中取消了红线组合 在JDK14中绿线会被删除 CMS和PSGC框架不同不可以一起使用PNGC和PSGC性能差不多 CMS:不能是在老年代空间满的时候进行使用需要提前进行回收因为CMS是并发的他在回收的时候用户线程还在执行用户线程还有可能制造新的垃圾所以需要提前进行回收那如果说回收的比较晚垃圾制造的速度比回收的速度还要快可能CMS回收失败一旦失败所以要使用SOGC作为备用方案赶紧把用户线程停下来进行全部GC 单核CPU是单线程垃圾收集器比多线程垃圾收集器要高因为防止进行大量的线程切换 Serial(新生代)Serial Old(老年代):单线程垃圾回收: 当新生代使用serial的时候老年代默认使用Serial Old他们在执行的时候必须停止所有的用户线程 -XX:PrintCommandLineFlags -XX:UseSerialGC表明新生代使用Serial GC老年代使用Serial Old GC 然后可以通过jps验证一下jinfo -flag UseSerialGC 进程的ID 缺点:串行垃圾回收器会导致验证的STW parNew新生代并行垃圾回收器和Serial Old(单线程穿行垃圾回收器)或者是CMS(老年代并行垃圾回收器)一起使用 在服务器端模式下是多核CPU的场景这个时候就不和客户端一样是一个单线程的垃圾回收器了服务器端硬件更多一些 在老年代可以使用CMS或者是Serial Old在JDK9中Serial Old不能再和ParewNew使用了在JDK14CMS也被移除了这个时候ParNew就比较尴尬了 对于新生代使用多线程垃圾回收器使得GC的时间更短垃圾回收更高效STW时间更短 但是在老年代标记整理算法效率比较差涉及到内存碎片整理 单CPU:同一时刻只能由一个线程执行 设置线程数量不要超过CPU核数放置多个线程抢夺CPU和CPU核数相同越好 -XX:PrintCommandLineFlags -XX:UseParNewSerialGC -XX:UseConcMarkSweepGC