需要反复记忆的东西--暂时无用

一  JVM

     1.  栈帧包括:局部变量表、操作数栈、常量池、动态链接、方法出口信息等

     2.  方法区:堆的一个逻辑部分,线程共享,存放已经被虚拟机加载的类信息、常量、

                        静态变量、即时编译器编译后的代码等。

     3.  如何判断对象已死

          对象不可达并经历两次标记的对象。

        (1)可达性分析算法

                 该算法的思路是通过一系列称为“GC Root”的对象作为始点,从这些节点开始向下搜索,

                 搜索所走过的路称为引用链(Reference Chain),当一个对象到GC Roots没有任何引

                 用链相接(不可达)时,则证明此对象时不可用的。如下图所示,object5、object6、

                 object7虽然相会关联,但是它们到GC Root是不可达的,所以将会被判定为是可回收

                 的对象。

                 

                在Java中,可作为GC Root的对象包括下面几种:

                --  虚拟机栈(栈帧中的本地变量表)中引用的对象。

                --  本地方法栈中JNI(即一般说的Native方法)引用的对象

                --  方法区内类静态属性引用的对象和常量引用的对象。

          (2)可达性分析算法            

                   如果进行垃圾回收的时候发现一个对象没有在GC Root链上,那么就需要进行两次的

                   标记过程,如果当前发现没有关联在GC Root链上,那么就会进行第一次标记,如果

                   此时对象的finalize()方法没有被覆盖或该方法已经被虚拟机调用过,那么此时将被标

                   记为没有必要执行,此时该对象会被放入“即将回收”集合,否则就会放入F-Queue的

                   对象中等待执行finalize()方法,如果在此方法中对象将自己与GC Root链上的任何一

                   个对象关联(譬如把自己(this)赋值给某个类变量或者对象的成员变量),那么就

                   会被移出”即将回收“集合。

     4.  常见的垃圾回收算法

        (1)标记清除算法

                         分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回

                         收被标记的对象所占用的空间,该算法最大的问题是内存碎片产生严重。

        (2)复制算法

                        为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存

                        划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复

                        制到另一块上去,把已使用的内存清掉。这种算法虽然实现简单,内存效率高,不

                        易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多

                        的话,Copying算法的效率会大大降低

        (3)标记压缩算法

                         标记阶段和标记算法相同,标记后不是清理对象而是将存活的对象移动到内存的

                         一端,然后清除边界外的对象。

        (4)分代算法

                        分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同

                        生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代

                        (Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃

                        圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃

                        圾需要被回收,因此可以根据不同区域选择不同的算法。新生代采用复制算法,

                        老年代采用标记压缩算法。

        (5)分区算法

                         将整个内存分为N个小的独立空间,每个小空间都可以独立使用,这样细粒度控

                         制一次回收多少个小空间和哪些个小空间,而不是对整个空间进行GC,从而提

                         升性能。

     5.  垃圾回收器

         (1)Serial收集器

                           新生代收集器,是一个单线程的收集器,采用的是“复制算法”。它的 “单线程”

                           的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工

                           作,更重要的是在它进行垃圾收集时,必须暂停其它所有工作线程,直到它收

                           集结束。

         (2)ParNew收集器

                           是Serial收集器的多线程版本,采用的是“复制算法”。除了使用多线程进行垃圾

                           收集之外,其余行为包括Serial收集器可用的所有控制参 数、收集算法、

                           Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样,在

                           实现上,这两种收集器也共用了相当多的代码。ParNew收集器除了多线程收集

                           之外,其它与Serial收集器相比并没有太多创新之处,但它却是运行在Server模

                           式下的虚拟机中首选的新生代收集器,其中一个与性能无关但很重要的原因是,

                           除了Serial收集器外,目前只有它能与CMS收集器配合工作。

         (3)Parallel Scavenge收集器:

                          是工作在新生代的垃圾回收器,使用了复制算法,也是多线程独占(独占回收器:

                          GC来的时候应用停顿只执行GC)形式的收集器,它的特点是关注系统的吞吐量。

         (4)Serial Old收集器:

                          是Serial收集器的老年代版本,它同样是一个单线程的收集器,使用“标记-整理”

                          算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用。如果在

                          Server模式下,那么它主要还有两大用途:

                          一种用途在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用。

                          另一种用途就是作为CMS收集器的后备方案,在并发收集器发生

                          Concurrent Mode Failure时使用。

         (5)Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

                   这个收集器在JDK1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器

                   一直处于比较尴尬大的状态。原因是如果新生代选择了Parallel Scavenge收集器,老

                   年代除了Serial Old收集器外别无选择。由于老年代Serial Old收集器在服务端性能上

                   的拖累,使用Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大的效果。

                   直到。

        (6)CMS回收器:

                 工作在老年代的回收器,使用的是标记清除法,是一种以获取最短回收停顿时间为目标

                 的收集器主要关注的是系统的停顿时间。CMS并不是独占的回收器,也就是说CMS回

                 收的过程中,应用程序仍然在不停的工作,又会有新的垃圾不断产生,所以在使用CMS

                 过程中应该确保应用程序的内存足够可用,CMS不会等到应用程序饱和的时候在去回收

                 圾,而是到达某个阈值的时候就去回收,回收的阈值可以通过指定的参数来设置

                 -XX:CMSInitiatingOccupancyFraction来指定,默认值是68,也就是说当老年代的使用率

                 达到68%的时候会执行CMS垃圾回收,如果内存使用增长的很快,在CMS过程中出现了

                 内存不足的情况,CMS回收就会失败,虚拟机将启用老年代串行回收器进行垃圾回收,

                 这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC停顿时间可能

                 过长,所以设置-XX:CMSInitiatingOccupancyFraction要根据实际情况。

                 运作过程:初始标记、并发标记、重新标记、并发清除。有的版本叫(另外CMS收集

                 器比较详细的步骤查看:https://blog.csdn.net/zqz_zqz/article/details/70568819

                 初始标记、重新标记这两个步骤仍需“Stop The World”。初始标记仅仅只是标记一下

                 GC Root能直接关联到的对象不包括间接到达的哈,没有直接关联的对象在并发标记

                 阶段标记哈),速度很快;并发标记阶段就是进行GC Roots Tracing的过程重新标记

                 则是为了修正并发标记期间因用户程序继续而导致标记产生变动的那一部分对象的标记

                 记录,这个时间的停顿时间一般会比初始标记阶段稍长些,但远比并发标记短。在这个

                 阶段暂停所有用户线程,重新扫描堆中的对象,进行可达性分析,标记活着的对象,特

                 别需要注意一点,这个阶段是以新生代中对象为根来判断对象是否存活的;并发清除,

                 这个阶段主要是清除那些没有标记的对象并且回收空间,由于CMS并发清理阶段用户线

                 程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标

                 记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这

                 一部分垃圾就称为“浮动垃圾”。

                 缺点:

                 (A)CMS收集器对CPU资源非常敏感(其实应该说它比较消耗cpu资源)。在并

                           发阶段,它虽然不会导致用户线程停顿,但是会因为占用一部分CPU资源而

                           导致应用程序变慢。CMS默认启动的回收器线程数量是 (CPU数量+3) / 4,

                           也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU

                           资源,并且随着CPU数量的增加而下降。但是当CPU的数量不足时(譬

                           如2个),CMS对用户程序的影响就可能变得很大。为了应付这种情况,

                           虚拟机提供了一种称为“增量式并发收集器”(i-CMS)的CMS收集器变种,

                           就时在并发标记、清理的时候让GC线程、用户线程交替运行,尽量减少线

                           程的独占资源的时间。在目前版本中i-CMS已经被声明为“deprecated”,即

                           不在提倡用户使用。

                 (B)CMS处理器无法处理浮动垃圾。

                 (C)基于“标记-清除”算法实现的收集器,会产生大量额空间碎片,会出现并发

                           模式失败的情况,并发模式失败就要执行内存整理(内存压缩),这种情

                           况下会导致CMS比Parallel Scavenge更慢(通常情况下CMS比

                           Parallel Scavenge要快)。

        (7)G1回收器:

                 是JDK1.7中提出的基于“标记-整理”算法的垃圾回收器,它的使命是取代CMS

                 回收器,可建立可预测的停顿时间模型属于分代垃圾回收器 区分新生代和老

                 年代,依然有eden和from、to区,它不要求新生代、老年代、eden、from、

                 to区空 间都 连续,使用了分区算法。据说JDK1.7之后使用的是G1回收算法,

                 不过有待考证,目前不能确定,但是在

                 JDK1.7里它还不太成熟。

                        G1收集器将整个Java堆划分为大小相等的独立区域(Region),虽然还

                 保留着新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它

                 们都是一部分Region(不需要连续)的集合。

                        G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划

                 地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个堆里面的垃圾

                 堆积的价值大小(回收所获得的空间大小以及回收所需要时间的经验值),

                 在后台维护一个优先列表,每次根据允许的回收时间,优先回收价值最大

                 的Region。

                 特点:

                 并行和并发:G1充分利用多CPU、多核环境下的硬件优势,使用多个CPU

                                      来缩短Stop-The-World停顿的时间,部分其它收集器原本需要

                                       停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的

                                        方式让Java程序继续执行。

                 分代收集:存在Eden、from、to。不需要其它收集器的配合就能独立管理

                                   整个GC堆,可以自己处理堆中的对象。

                 空间整合:重整体来看是基于“标记-整理”算法实现的;局部(两个Region

                                   之间)来看是基于“复制”算法的实现的,但这两种算法都不会

                                   产生内存碎片。

                 可预测停顿:能建立可预测的停顿时间模型。能让使用者明确指定一个长

                                      度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超

                                      过N毫秒。(这点是G1相对于CMS的另外一大优势,低停

                                      顿是G1和CMS共同的关注点,但G1除了追求低停顿外,还

                                      能建立可预测的停顿时间模型)

                 执行过程:如果不计算维护Remembered Set的操作,G1收集器的运作

                                   大致可划分为以下几个步骤:

                                   初始标记、并发标记、并行标记、最终标记、筛选回收。

                                   初始标记仅仅是标记一下GC Root能直接关联到的对象,并

                                   且修改TAMS(Next Top at Mark Start)的值,让下一阶段

                                   用户程序并发运行时,能在正确可用的Region中创建对象,

                                   这阶段需要停顿但时耗时短;并发标记是从GC Root开始对

                                   堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,

                                   但可与用户程序并发执行;最终标记是为了修正在并发标记期

                                    间因用户程序继续运作而导致标记产生变动的那一部分标记记

                                    录;筛选回收阶段首先要对各个Region的回收价值和成本进行

                                   排序,根据用户所期望的GC停顿时间来制定回收计划,根据

                                   Sun公司透露的信息来看,这个阶段其实可以做到与用户程序

                                   一起并发执行。

  

     6.  减少GC开销的编程技巧

           http://www.importnew.com/10472.html

         (1)不要显式调用System.gc()

                  此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它

                  会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

         (2)尽量减少临时对象的使用

                  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于

                  减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时

                  间,减少了主GC的机会。

         (3)尽量使用StringBuffer,而不用String来累加字符串

             由于String是固定长的字符串对象,累加String对象时,并非在一个

                  String对象中扩增,而是重新创建新的String对象,如

                  Str5=Str1+Str2+Str3+Str4,这条语句执行过

                  程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的

                  String对象,但这

                  些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。

        (4)计划好List的容量

                 像ArrayList这样的动态集合用来存储一些长度可变化数据的基本结

                 构。ArrayList和一些其他的集合(如HashMap、TreeMap),底层

                 都是通过使用Object[]数组来实现的。而String(它们自己包装在

                 char[]数组中),char数组的大小是不变的。那么问题就出现了,

                 如果它们的大小是不变的,我们怎么能放item记录到集合中去呢?

                 答案显而易见:分配更多的数组。所以,无论什么时候,尽可能

                 的给List或者Map分配一个初始容量,就像这样。

        (5)能用基本类型如Int,long,就不用Integer,Long对象

                 基本类型变量占用的内存资源比相应对象占用的少得多,如果没有

                 必要,最好使用基本变量。

        (6)尽量少用静态对象变量

                 静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

        (7)对象不用时最好显式置为Null

                 一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显

                 式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效

                 率。

        (8)分散对象创建或删除的时间(个人觉得这个似乎不太现实)

                 集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大

                 量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或

                 整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一

                 样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从

                 而大大增加了下一次创建新对象时强制主GC的机会。

posted @ 2019-05-26 18:43  jialanshun  阅读(223)  评论(0编辑  收藏  举报