当前位置: 首页 > news >正文

中文电商网站模板南宁网站建设业务员

中文电商网站模板,南宁网站建设业务员,WordPress托管如果使用插件,国外网站设计师一)什么是垃圾#xff1f; 垃圾指的是在应用程序中没有任何指针指向的对象#xff0c;这个对象就是需要被回收的垃圾#xff0c;如果不及时的针对内存中的垃圾进行清理#xff0c;那么这些垃圾对象所占用的内存空间可能一直保留到应用程序结束#xff0c;被保留的空间无法… 一)什么是垃圾 垃圾指的是在应用程序中没有任何指针指向的对象这个对象就是需要被回收的垃圾如果不及时的针对内存中的垃圾进行清理那么这些垃圾对象所占用的内存空间可能一直保留到应用程序结束被保留的空间无法被其他对象使用甚至有可能导致内存溢出 JVM规范说了并不需要必须回收方法区不具有普遍性元空间使用的是JVM之外的内存 二)如何判断一个对象是否是垃圾: 2.1)引用计数器: 引用计数器优点:实现简单垃圾对象便于标识判断效率高回收没有延迟性 引用计数器缺点:效率要比可达性分析要强随时发现随时回收实现简单但是可能存在内存泄漏 1)它需要单独的字段存储计数器这样的做法增加了存储空间的开销 2)每一次赋值都是需要更新计数器伴随着加法和减法的操作 3)引用计数器又一个严重的问题就是无法处理循环引用的问题这是一条致命缺陷 虽然在JAVA中没有使用循环引用但是Python中使用了两种方法解决了这个问题: 1)手动解除很好理解就是在合适的时机接触引用关系 2)使用弱引用weakref 2.2)可达性分析: 局部变量表静态引用变量通过引用链关联的引用链是不会被回收局部变量表天然作为GCROOTS就是只是进行新生代回收的时候老年代的引用也可以作为GCROOTS 1虚拟机栈中引用的对象(栈帧中的本地方法表) 2)方法区中(1.8称为元空间)的类静态属性引用的对象 一般指被static修饰的对象加载类的时候就加载到内存中 3)方法区中的常量引用的对象。 4)本地方法栈中的JNInative方法引用的对象。 注意即使可达性算法中不可达的对象也不是一定要马上被回收还有可能被抢救一下要真正宣告对象死亡需经过两个过程: 1)可达性分析后没有发现引用链 2)查看对象是否有finalize方法如果有重写且在方法内完成自救[比如再建立引用]还是可以抢救一下注意这边一个类的finalize只执行一次这就会出现一样的代码第一次自救成功第二次失败的情况。[如果类重写finalize且还没调用过会将这个对象放到一个叫做F-Queue的序列里这边finalize不承诺一定会执行这么做是因为如果里面死循环的话可能会时F-Queue队列处于等待严重会导致内存崩溃这是我们不希望看到的。如果要是使用可达性分析算法来判断内存是否要进行回收那么分析工作必须要在一个能够保持一个一致性的快照来进行这一点不满足的话分析结果的准确性就无法保证这一点也就是GC必须进行STW的一个重要原因即使是号称几乎不会发生停顿的CMS垃圾回收器枚举根节点的时候也是必须要停顿的 三)对象的finalize方法详解: 1)JAVA语言提供了对象终止机制来允许开发人员提供对对象销毁之前的自定义处理逻辑当垃圾回收器发现没有引用指向一个对象的时候就是垃圾回收器在进行回收此对象之前总是会调用这个对象的finalize方法当垃圾回收器发现没有任何一个引用指向该对象的时候总是会调用这个对象的finalize()方法finalize()方法允许在子类中被重写用于在垃圾回收时进行资源释放和垃圾清理的工作关闭文件套接字和数据库连接等等 2)永远不要试图调用某一个对象的finalize方法应该交给垃圾回收器来调用 2.1)finalize方法可能会导致对象复活 2.2)finalize()方法执行时间是没有保障的他完全由GC线程所决定极端情况下如果不发生GC那么一个糟糕的finalize()方法会影响程序的执行性能 如果说所有的根节点都无法访问到某一个对象说明该对象已经不再被使用了一般来说此对象需要被回收但事实上也并非是非死不可的这个时候他们暂时处于唤醒状态一个无法触及的对象很有可能在某一个条件下复活自己如果这样没那么对于他的回收就是极其不合理的为此定义虚拟机中的对象可能的三种状态: 1)可触及的:从根节点开始可以到达这个对象 2)可复活的:对象的所有引用都被释放但是对象很有可能在finalize()中复活 3)不可触及的:对象的finalize()方法被调用并且没有复活那么就会进入到不可及状态不可触及的对象不可能复活因为finalize()方法只会被调用一次以上三种状态中是由于finalize()方法的存在进行的区分只有在对象不可触及的时候才可以被回收 3)所以说判断一个对象是否可以进行回收至少要经历两次标记过程 3.1)如果说对象A到Gcroots不存在引用链那么就进行第一次标记 3.2)如果筛选进行判断对象是否执行了finalize()方法 a)如果对象没有重写finalize()方法或者是finalize()方法已经被虚拟机调用过那么虚拟机不会再重新调用该方法直接该对象就被标记成不可达的 b)如果对象A重写了finalize()方法还没有被执行过那么该对象会被插入到一个队列中这是由虚拟机自动创建的低优先级的finalizer线程触发其finalizer方法执行 c)finalize()方法是对象进行逃脱死亡的最后机会稍后GC就会对队列中的对象做第二次标记如果该对象和引用链上面的任意一个对象建立了联系那么在第二次标记的过程中此对象会被移出即将回收的集合之后对象会再次出现没有引用存在的情况在这种情况下fnalize()方法不会被再次调用对象会直接变成不可触及的状态 代码执行两次一次分为finalize()方法没有被注释一种有注释 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时系统整体内存不足时等 4.1)标记清除算法: 标记是非垃圾的对象就是可达的对象然后清除的是垃圾对象要先递归进行遍历所有可达对象然后清除的时候需要再开始遍历一遍整个内存空间还需要进行维护空闲列表 就比如说我们的硬盘只要你不小心点击了格式化此时也不是真正的进行格式化只是标记性删除但是千万不要再向里面存放数据因为数据会覆盖就不好恢复了 当堆中有效的空间被耗尽的时候就会停止整个程序进行STW首先进行标记然后进行清除 1)标记:从引用根节点进行标记标记所有可达的对象也就不是垃圾的对象一般是在对象的header头里面标记成可达的对象 2)清除:对整个堆内存进行从头到尾的进行线性的遍历如果发现某一个对象在header中没有被标记那么直接将其回收 缺点:效率不算太高在进行GC的时候需要终止整个应用程序用户体验差还有就是这种方式进行清理出来的空闲内存不是连续的而是会产生内存碎片还需要维护一个空闲列表 注意这里面的清空并不是真正的清空而是需要把消除的对象的地址保存在一个空闲列表里面下次有新的对象需要加载的时候要判断垃圾的位置空间是否足够如果够就进行存放 4.2)标记整理算法:内存利用率贼低 首先经过可达性分析在A区找到可达的对象一旦找到了可达的对象就不需要进行标记直接将可达的对象进行复制算法放到另一块区域B另一块空间的所有区域B的对象都是连续的 将活着的内存空间分为两块每一次只是用其中一块再进行垃圾回收的时候将正在使用的内存中的存活对象复制到还没有被使用到的内存快里面然后最后清楚正在使用到的内存块中的所有对象交换两个内存的角色最后完成垃圾回收 1)因为在新生代对于常规应用的垃圾回收一般情况下是可以回收很多的内存空间的回收性价比就比较高 2)没有标记和清除过程实现简单运行高效 3)复制过去以后保证空间的连续性不会出现内存碎片问题 缺点:维护引用和对象的地址映射 1)需要两倍的内存空间 2)回收的对象比较少剩余存活的对象比较多那么移动的对象比较多但是还要大量维护指针和对象的关系老年代不适合使用复制算法因为很多对象都不死老年代复制对象开销太大 3)对于G1这种拆分成大量的Regin的GC复制而不是移动意味着GC需要维护Regin之间的对象引用关系不管是内存占用还是空间开销也不少如果系统中的垃圾对象很多复制算法需要复制的存活的数量不算太大或者说非常低才可以 4.3)标记整理算法: 从根节点标记所有被根节点引用的对象将所有的存活对象压缩到内存的一端按照顺序进行存放最后清除所有边界以外的空间还要移动位置还要修改引用对象关系很麻烦这个算法比标记清除算法效率还低 标记压缩算法的最终效果就是等同于标记清除算法执行完成以后再来进行一次碎片整理二者的本质差异就是标记清除算法是一种非移动式的回收算法标记压缩算法是非移动式的是否移动回收后的存活对象是一项优缺点并存的风险策略还可以看到标记的存活对象会被清理需要按照内存地址进行依次排列而没有被标记的内存会被回收掉清理掉如此一来JVM在进行分配内存空间的时候JVM只是需要维护一个内存的起始地址就可以了这笔维护一个空闲列表节省了很多开销 优点:消除了标记清楚算法中的的内存区域分散的特点我们需要给新对象分配内存的时候JVM只是需要持有一个内存的起始地址即可消除了复制算法中内存减半的高额代价 缺点:从效率上来说标记整理算法要低于复制算法移动对象的时候如果对象被其他引用所指向还需要调整引用的地址移动过程中需要全程暂停用户应用程序 4.4)分代回收: 1)在前面的这些算法中没有一个算法可以完全代替其他算法它们都具有自己独特的优势和特点分代收集算法应运而生分代收集算法是基于这样一个事实不同对象的生命周期是不一样的因此不同生命周期的对象可以采取不同的收集方式一边用力啊提升回收效率一般是吧JAVA堆分成新生代和老年代这样就可以根据各个年代的特点使用不同的回收算法来提升垃圾回收的效率 2)在JAVA程序运行的过程中会产生大量的对象其中有一些对象是和业务信息相关比如说Http请求的session对象线程Socket连接这类对象和业务直接挂钩因此生命周期比较端比如说String对象由于不可变的特性系统会产生大量的这些对象甚至有的对象只使用一次就被回收新生代:老年代1:2edin区:幸存者1区:幸存者2区 3)目前几乎所有的GC都是采用粉黛收集算法来执行垃圾回收的在HotSpot虚拟机中基于分代的概念GC所使用的内存回收算法必须结合年轻代和老年代各自的特点 年轻代: 区域相比于老年代较小对象的生命周期比较短存活率低回收比较频繁这种情况复制算法的回收整理速度是最快的渎职算法的效率值和当前存活对象有关因此很是适合年轻代的回收而复制算法解决的是内存利用率不高的问题通过两个幸存者区得到缓解 老年代: 区域比较大对象的生命周期比较长存活率高回收不及年轻代频繁这种情况存在大量存活度高的对象复制算法明显是非常不合适的一般是由标记整理或者是标记清楚算法的混合实现标记阶段的开销和存活对象的数量成正比清除阶段的开销和所管理区域的大小成正比整理阶段的开销和存活对象的数据成正比 标记的开销和存活的对象成正比因为标记只能标记存活的对象 清除阶段要进行全堆空间线性的遍历压缩或者是整理和存活对象的大小成正比 以HotSpot中的CMS垃圾回收器为例CMS是基于标记压缩清除来实现的对于对象的回收效率很高但是对于碎片问题CMS会使用基于标记压缩算法的Serial Old回收器作为补偿机制当内存回收不佳的时候将采用Serial Old执行Full GC来达到对于老年代内存的管理 对于STW的理解: 先确定GCROOTS枚举根节点此时要进行Stop The World确保数据的一致性 stop the world停止的是用户线程就是为保证一致性 可达性分析算法中枚举根节点会导致所有Java执行线程停顿 衡量一个垃圾回收器的标准就是吞吐量和低延迟 4.5)增量收集算法: 用户暂停时间/垃圾回收时间 吞吐量:工作线程一共的执行业务的时间 比如说我现在有一个房子我一直不进行清理一直制造垃圾直到三个月之后才清理一次此时清理的时间就比较长阻隔用户线程的时间就比较长但是如果说隔一会清理一会效果就会比较好用户线程和回收线程协调交替执行看样子就是并发的执行从而到达一种低延迟的行为就是为了让用户感觉好一点 被STW中断的应用程序线程会在完成GC之后恢复频繁的中断会让用户感觉像是网速不快造成的电影卡顿一样,CMS自称低延迟开发中不要显示的进行GC导致STW 就是类似于洗衣服假设现在我只是做两件事情:在宿舍给别人讲题和在卫生间洗衣服 工作线程:在宿舍给别人讲题 GC线程:在卫生间洗衣服 吞吐量:给宿舍讲题时间越长吞吐量越高 停顿时间:在卫生间洗衣服越长STW时间就越长 如果想要达到极高的吞吐量那么就少去卫生间洗衣服一次洗的多一点这样吞吐量就特别高 停顿时间:多去洗衣服每一次一会就回来这样会使停顿时间最短不让舍友等待时间过长但是存在着从宿舍去卫生间和从卫生间回到宿舍时间的开销会降低讲题总时间 4.5)分区算法降低停顿时间主要是保证低延迟而不是吞吐量 有的分区存放大对象有的区域存放小对象回收区域的个数取决于时间的长短可以控制可控时间一般来说在相同条件下堆空间越大一次GC所需要时间就越长有关GC产生的停顿时间也是越长的为了可以更好地控制GC产生的停顿时间可以将一块大的内存区域分割成多个小块内存区域根据目标的停顿时间每一次合理的回收若干个小区间而不是整个堆空间从而减少一次GC所产生的停顿 分代算法将按照对象的生命周期长短划分成两个部分分区算法是将整个堆空间划分成连续的不同的小区间每一个兄啊去见都独立使用独立回收这种算法的好处就是可以控制一次会回收多个小区间 五)System.gc()的理解: 1)提醒JVM垃圾回收器去执行GC但是不确实马上执行GC底层是调用RunTime().getTime().gc()进而也不能确定finlizle方法一定会被调用Full GC 就是收集整个堆包括新生代老年代和方法区但是调用System.gc系统做了免责声明GC具体干不干不怪这个方法仅仅是提醒JAVA虚拟机进行垃圾回收但是实际上是否进行垃圾回收System.GC()不保证仅仅起到一个提醒JVM虚拟机进行垃圾回收 2)在默认情况下通过System.gc()或者是RunTime.getRunTime().gc()的调用会显示的触发FullGC同时针对于老年代和新生代进行回收尝试释放被对其对象所占用的内存 3)然而System.gc()调用负责一个免责声明无法保证对于垃圾收集器的调用JVM实现者通过System.gc()来决定与JVM的GC行为垃圾回收应该是自动进行的无需手动进行特殊的情况下比如说现在正在编写一个性能基准可以在运行之间调用System.gc() 4)做性能测试之前先进行GC防止初始情况下一些因为内存不够的原因导致性能测试不太精准了局部变量表第一个位置存放的是this System.runFinalization(),//强制调用失去引用的对象的finallize方法 上面这个结果不会触发垃圾回收虽然已经过了作用域但是buffer的变量槽仍然没有被覆盖 此时buffer所指向的对象会被回收buffer作用域已经过了所以buffer肯定用不上了系统会判定buffer占用的slot为可覆盖的slot局部变量表的第一个位置永远是this一但value覆盖buffer所在的槽buffer引用被覆盖此时没有任何引用指向字节对象数组所以此时触发System.gc就会回收垃圾 六)内存溢出: 1)内存溢出相比于内存泄漏来说他也是引起程序崩溃的罪魁祸首之一由于GC在一直进行发展所有一般情况下除非应用长须占用的内存增长速度非常快造成垃圾回收已经跟不上内存消耗的速度否则不太可能出现OOM 2)大多数情况下GC会进行各种年龄段的垃圾回收实在是空间不够就来一次独占式的Full GC操作这个时候挥挥手大量的内存来供应用程序继续使用 3)OOM:没有空闲内存并且垃圾回收器也无法提供更多的空闲内存 导致栈溢出的最常见的情况就是死循环和无限递归方法自己调用自己这样JAVA虚拟机栈就会只是入栈而不进行出栈当到达JAVA虚拟机栈的最大数之后就会出现OOM异常 堆溢出的常见场景:代码中创建了大量大对象且长时间也没有被垃圾收集器收集 1)内存泄漏:最常见的情况就是内存泄漏就是对象在被创建以后不再被使用但是还没有被释放这样就会导致堆中的对象数量持续增加比如说ThreadLocal使用不当使用完成以后没有及时的调用remove方法导致内存泄漏已经忘记释放各种连接也会导致内存泄漏比如说数据库连接Socket连接以及IO连接等等 2)无限递归创建大对象:无限的调用一个方法可能会导致栈溢出但是如果递归方法中创建了大量的对象并且持续性地进行递归也可能会导致堆溢出 3)创建大量大对象:创建大量大对象比如说数组或者是集合可能会导致堆溢出如果没有连续的内存来存储大对象堆溢出就会发生 4)没有合理的设置堆大小:JAVA虚拟机的堆内存设置不够堆的大小不合理没有显式地指定堆的大小或者是指定数值偏小 方法区溢出: 1)因为永久代的大小是有限的并且JVM对于永久代垃圾回收比如说常量池回收卸载不再需要的类型非常不积极所以当我们不断添加新类型的时候永久代出现OOM已非常正常类似于intern字符串缓存占用太多空间也会导致OOM问题: java.lang.OutOfMemory:permGen space和但是随着元空间的引入方法去内存已经不再那么窘迫所以相应的OOM有所改观出现OOM异常信息变成了:java.lang.OutOfMemory:Metaspace直接内存不足也会报OOM 实际上在抛出OOM异常之前通常来说垃圾收集器会被触发尽可能地来进行清理出所用空间比如说在引用机制分析过程中涉及到JVM会进行回收软引用所指向的对象 Java堆溢出一般是JVM堆内存设置不合理或者是内存泄漏导致的: 如果是内存泄漏那么就可以通过工具查看泄露对象到GCROOTS的引用链掌握了泄露对象的类型信息以及GC ROOTS的引用链信息就可以精准地找到泄露代码出现的位置 如果不是内存泄漏那么就是内存中的对象都还是必须地存活者那么就应该检查虚拟机的堆参数查看你是否可以将虚拟机的内存调大些 七)内存泄漏的原因:   严格的来说只有对象不被使用到了但是GC又不能将他们进行回收的情况才叫做内存泄漏但是很多情况下一些不太好的实践或者是疏忽会导致对象的生命周期变长甚至有可能导致OOM也可以叫做宽泛意义上的内存泄漏尽管内存泄露不会导致程序立即崩溃但是一旦发生内存泄漏程序中的可用内存可能就会步步蚕食直至耗尽所有内存最终出现OOM 1)宽泛意义上的内存泄漏:其实可以把对象定在方法内部作为局部变量当方法执行完成以后对象就要被回收了但是如果将这个变量成员变量那么这个对象的存活周期就变得很长但是如果是这个变量静态变量还是大对象类变量随着类的加载而加载随着类的消亡而消亡也会理解成宽泛的内存泄露还比如说Session会话不使用就没有必要存放内存泄漏可能会导致内存溢出如果出现很多生命周期很长的对象再加上很多没有办法回收的数据的存在就很有可能造成内存泄露 2)存在很多生命周期很长的对象而本身生命周期没有这么长的对象而又生命很长可以称之为是内存泄漏 严格意义宽泛意义上的内存泄漏有些对象不使用但是还存着引用链有可能忘记断开引用如右图对象没有用处但是还存在引用链可能造成内存回收ThreadLocal 1)单例模式中的对象是static单例对象的生命周期和应用程序是一样长的一个进程只有这一个实例就比如说RunTime实例声明的是静态的每一个进程只有一个实例会随着程序的执行而产生随着进程的结束而销毁如果此时单例对象关联了一个外部的很大的对象这个外部对象用一会就不用了单例对象的生命周期非常长但是这个引用关系又断不掉所以此时连带着外部对象的生命周期也很长本身又不用但是无法释放内存此时这个对象还无法回收引用链条得不到释放 2)当内部资源外部资源需要交互的时候是需要进行手动的关闭资源链接没有关闭资源GC就无法回收这些对象只有当程序结束的时候才能回收这些链接的对象此时可能就会发生内存泄漏 八)内存泄露的常见场景: 1)静态集合类:静态集合类比如说HashMapLinkedList如果这些容器是静态的那么他们的生命周期和JVM的保持一致那么容器中的对象在程序结束之前不能被释放从而造成内存泄露简单来说长的生命周期的对象持有着短的对象的生命周期的引用尽管短的生命周期的引用的对象不再进行使用但是却因为长的生命周期的对象持有者它们的引用而不能被回收: ListObject listnew ArrayList();public void run(){Object objnew Object();list.add(obj);} 2)单例模式:和上面的情况一样和静态集合导致的内存泄漏的原因是相似的因为单例的静态特性它的生命周期和JVM的生命周期是一样长的所以说如果单例对象如果持有着外部对象的引用那么这个外部对象也不会回收那么就容易造成内存泄露 3)内部类持有外部类:内部类持有外部类如果一个外部类的实例对象的方法返回了一个内部类的实例对象那么这个外部类的对象不会被垃圾回收会造成内存泄漏 4)各种链接数据库连接网络连接: 5)变量不合理的作用域: 6)改变哈希值: 九)如何排查OOM解决OOM:有些对象生命周期太长其实没有必要 1)要想解决OOM或者是堆空间溢出的问题一般的时候是通过内存映像分析工具(Ecplice Memory Analyzer)来针对于dump出来的堆转存储内存快照进行分析重点是进行确认内存中的对象是否是有必要的也就是说要仔细分析看看到底是出现了内存溢出还是内存泄漏 OOM通常是堆空间或者是元空间更多的时候是堆空间需要导出各个堆内存空间使用的快照dump文件通过jvisvm或者是jpofile工具可以解析dump文件看啊可能是否是出现了内存泄漏还是内存的溢出 2)如果是内存泄漏可以进一步通过工具来查看泄露对象到GCROOTS的引用链于是就找到泄露对象是通过了什么样的路径和GCROOTS相关联并导致垃圾回收器无法回收它们掌握了泄露对象的类型信息以及GCRROTS引用链的关系就能精准的确定出泄露代码的位置 3)如果不存在内存泄露换句话来说就是内存中的对象确实还必须都存活着那么此时就应该检查虚拟机的堆参数和机器的物理内存相比看看是否还可以调大从代码的角度上来查 4)看是否存在某些对象生命周期过长持有状态的时间过长尝试减少运行期间的内存消耗 看看有没有内存泄漏的问题要及时地找到内存溢出的对象将他的指针给断掉查看引用链 如果不是内存泄漏那么就适当提升方法区和堆空间大小的参数再看看某些对象的生命周期是否合理有些对象没有必要静态就不要静态有些对象只是在方法内使用就没有必要再方法外声明等等调整对象的生命周期及时的进行GC 十)程序中的并发和垃圾回收过程中的并行和并发: 程序中的并行和并发(一个CPU快速切换CPU不是真正意义上的同时执行): 并行就是在具体某一个时刻的时候有三个线程同时的进行执行主要是取决于多核CPU而并发在某一个时间点上面只能有一个进程在执行在时间段内是可能有多个线程在切换执行 下面的绿线表示用户线程红色表示垃圾回收器串行垃圾回收器很慢   十一)安全点和安全区域(了解) 1)用户线程在进行执行的时候并非在所有地方都是能够停下来进行GC的也就是说用户线程不是可以在所有的位置停下来进行STW的安全点的选择是很重要的如果安全点太少可能会导致GC的停顿时间太长可能GC线程等待太久而导致OOM如果太频繁可能会导致性能问题因为如果savepoint太多经常执行垃圾回收可能造成线程频繁切换导致性能问题 2)通常会选择是否让程序具有长时间运行的特征为标准比如说选择一些执行时间比较长的指令作为Safe Point比如说方法调用循环跳转和异常跳转 3)安全点比较少这个时候GC等待时间太长用户线程执行时间过长还有可能会导致OOM点太多STW时间也会变长这个时候切换线程开销很大 4)最好是在跳转的时候或者调用新方法的时候执行时间比较长比如说在进行方法调用的时候要将方法压入虚拟机栈把它们作为savepoint最好不要在程序指令执行很快的时候设置saveponintsleep和blocking引用关系也不会发生变化防止影响性能 安全区域: 线程处于睡眠或者是阻塞状态这个线程无法响应JVM中断请求此时你在让去走到中断安全点挂起是不可能的不可能唤醒吧 安全区的对象引用关系不会发生变化因此安全区域发生GC程序依然会继续执行 要出安全区域了但GC没有结束程序会等待安全区域应该是并发的但不能走出安全区域 1)当线程运行到Safe Regin的代码的时候首先表示已经进入了Safe Regin如果这段时间发生GCJVM会忽略表示为Safe Regin状态的线程 2)当线程即将离开Safe Regin的时候会检查JVM是否已经完成GC如果完成了那么继续运行否则线程必须要等待直到收到了Safe Regin的信号为止 十二)JAVA中的引用类型: 引用:前提是引用关系在的情况下都是可达的没有引用关系啥引用都会被回收 软引用:当内存足够的时候能保留在内存中当内存空间不足的时候那么直接可以抛弃这些对象在JAVA.lang.ref中可以直接使用 强引用可以直接访问目标对象强引用所指向的对象在任何时候都不会被系统回收虚拟机宁愿抛出OOM异常也不会强制回收强引用所指向的对象强引用最有可能导致内存泄漏 软引用:内存足够不会回收可触及的也就是软引用可达的对象当堆内存不够的时候才会回收软引用是不会报内存溢出的Mybatis内部缓存就是用到软引用 此时如果说刚好能容得下大数组大对象还刚好容不下这个弱引用不一定说回收软引用就会发生OOM不一定报OOM之前才会回收在JAVA1.2中提供了java.lang.SoftReference来实现软引用 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的时候,垃圾回收器会回收软引用的可达对象} } 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());//防止强引用来干扰} 弱引用:非必需的对象 软引用不如弱引用回收的快因为软引用要使用算法判断内存是否不足 1)虚引用:一旦将弱引用回收就会将虚引用存放到引用队列中可以追踪垃圾回收过程虚引用的作用主要是将回收的对象放在队列中 进行GC对象追踪 2)虚引用也被称之为是幽灵引用或者是幻影引用是所有引用类型中最弱的一个 3)一个对象是否有虚引用的存在完全不会决定对象的生命周期如果一个对象持有虚引用那么这个对象和没有引用指向是一模一样的随时都有可能被垃圾回收器所回收它不能被单独使用也不能通过虚引用来直接获取到被引用的对象当时图尝试通过虚引用的get()方法获取到对象的时候总是null为对象设置一个虚引用的目的就是为了在于追踪一个对象的垃圾回收过程比如说能在这个对象被垃圾回收的时候收到一个系统通知 4)虚引用必须和引用队列一起使用虚引用在进行创建的时候必须提供一个引用队列作为参数当垃圾回收器准备回收这个对象的时候如果发现她还存在虚引用那么就会再回收对象以后将这个虚引用加入到引用队列来通知应用程序对象的回收情况由于虚引用可以追踪对象的回收时间因此也可以将一些资源释放的操作在虚引用中进行执行和记录 5)在JDK1.2中提供了PhantReference类来实现虚引用 Object objnew Object();ReferenceQueueObject queuenew ReferenceQueue();PhantomReferenceObject referencenew PhantomReference(obj,queue);objnull;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
http://www.tj-hxxt.cn/news/230452.html

相关文章:

  • 安全网站开发获取整个网站源码工具
  • 关于企业的网站wordpress 分类页模板
  • 建站行业都扁平化设计网游开发培训
  • 摄影网站源码 免费下载营销型网站建设 兼职
  • 毕业设计做网站怎么答辩工程建设项目管理办法
  • 网站 源码 php导航网站 php
  • 台州网站优化排名大连建设网中标公司
  • 网站开发本科论文网站开发 制作
  • 这么建立com的网站最新logo设计大赛
  • 一键建站公司简单大气好记的公司名称
  • 网站建设 招标任务书邓州网络推广有效的方法
  • 东莞网站优化方式懂得都懂晚上正能量
  • 广州网站建设(信科网络)seo搜索引擎优化就业前景
  • 如何建设网站哪个济南兴田德润简介网页界面设计课程
  • 网站建设开发 脚本语言电子商务有哪些工作岗位
  • 网站在哪做商丘行业网站建设开发公司
  • 公司网站开发策略和基本步骤太原网站改版
  • joomla 2.5:你的网站建设_使用与管理 pdf100个成功营销案例
  • 欧洲美妇做爰网站网站上的文章用秀米可以做吗
  • 写男主重生做网站的小说个人网页设计尺寸是多少
  • 公司注册网站有什么好处wordpress 局域网 慢
  • 淘宝客网站做好了该怎么做汇鑫网站建设方便
  • 网站开发阶段流程php做网站自动生成前台吗
  • 域名注册和网站设计服务珠海关键词优化收费
  • 蓝色风格企业网站如何做公众号
  • 免费企业网站建设介绍wordpress进阶
  • 先做网站 先备案网站建设高清图
  • 网站设计推广微网站特效
  • 商业网站开发教程东圃做网站的公司
  • 文献综述 php网站开发云服务器网站崩溃的原因