zoukankan      html  css  js  c++  java
  • jvm-知识点总结

    参考:

    https://blog.csdn.net/wuqinghai2012/article/details/51485414

    http://pengjiaheng.iteye.com/blog/538582

    https://www.cnblogs.com/Dhouse/p/7839810.html

     

    jvm调优配置

    以下配置主要针对分代垃圾回收算法而言。

    堆大小设置

    年轻代的设置很关键

    JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。

    典型设置:

    java -Xmx3550m -Xms3550m -Xmn2g –Xss128k

    -Xmx3550m:设置JVM最大可用内存为3550M。

    -Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

    -Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

    -Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

     

    java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

    -XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5

    -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

    -XX:MaxPermSize=16m:设置持久代大小为16m。

    -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

     

    回收器选择

    JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

    吞吐量优先的并行收集器

    如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

    典型配置:

    java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

    -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。

    -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

    -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100

    -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

    n java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

    -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

     

    响应时间优先的并发收集器

    如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

    典型配置:

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

    -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。

    -XX:+UseParNewGC: 设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

    java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

    -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

    -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

     

    辅助信息

    JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:

    -XX:+PrintGC:输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]

    -XX:+PrintGCDetails:输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

    -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用 
    输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

    -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用。输出形式:Application time: 0.5291524 seconds

    -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用。输出形式:Total time for which application threads were stopped: 0.0468229 seconds

    -XX:PrintHeapAtGC: 打印GC前后的详细堆栈信息。输出形式:

    34.702: [GC {Heap before gc invocations=7:

    def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)

    eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)

    from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)

    to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)

    tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)

    the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)

    compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)

       the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)

    ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)

    rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)

    34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:

    def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)

    eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)

      from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)

      to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)

    tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)

    the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)

    compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)

       the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)

       ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)

       rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)

    }

    , 0.0757599 secs]

    -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

     

    【JVM】线上应用故障排查

    高CPU占用

    一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环。

     

    根据top命令,发现PID为28555的Java进程占用CPU高达200%,出现故障。

    通过ps aux | grep PID命令,可以进一步确定是tomcat进程出现了问题。但是,怎么定位到具体线程或者代码呢?

    首先显示线程列表:

    ps -mp pid -o THREAD,tid,time

    找到了耗时最高的线程28802,占用CPU时间快两个小时了!

     

    其次将需要的线程ID转换为16进制格式:

    printf "%x " tid

     

     

    最后打印线程的堆栈信息:

    jstack pid |grep tid -A 30

     

     找到出现问题的代码了!

     

    最后,总结下排查CPU故障的方法和技巧有哪些:

    1、top命令:Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。

    2、PS命令:Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。

    3、jstack:Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。

    4、pstack:Linux命令。可以查看某个进程的当前线程栈运行情况。

     

     

    高内存占用

    搞Java开发的,经常会碰到下面两种异常:

    1、java.lang.OutOfMemoryError: PermGen space

    2、java.lang.OutOfMemoryError: Java heap space

    要详细解释这两种异常,需要简单重提下Java内存模型。

     

    Java内存模型是描述Java程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。

    在Java虚拟机中,内存分为三个代:新生代(New)、老生代(Old)、永久代(Perm)。

    (1)新生代New:新建的对象都存放这里

    (2)老生代Old:存放从新生代New中迁移过来的生命周期较久的对象。新生代New和老生代Old共同组成了堆内存。

    (3)永久代Perm:是非堆内存的组成部分。主要存放加载的Class类级对象如class本身,method,field等等。

    如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:

    (1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。

    (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

    如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。

    一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。

     

    从代码的角度,软件开发人员主要关注java.lang.OutOfMemoryError: Java heap space异常,减少不必要的对象创建,同时避免内存泄漏。

    现在以一个实际的例子分析内存占用的故障排查。

    2G19({7(0}N(FIL09LH175N

    通过top命令,发现PID为9004的Java进程一直占用比较高的内存不释放(24.7%),出现高内存占用的故障。

    想起上一篇线上应用故障排查之一:高CPU占用介绍的PS命令,能否找到具体是哪个的线程呢?

    ps -mp 9004 -o THREAD,tid,time,rss,size,%mem

    1

    遗憾的是,发现PS命令可以查到具体进程的CPU占用情况,但是不能查到一个进程下具体线程的内存占用情况。

     

    只好寻求其他方法了,幸好Java提供了一个很好的内存监控工具:jmap命令

    jmap命令有下面几种常用的用法:

    •jmap [pid]

    •jmap -histo:live [pid] >a.log

    •jmap -dump:live,format=b,file=xxx.xxx [pid]

    用得最多是后面两个。其中,jmap -histo:live [pid] 可以查看当前Java进程创建的活跃对象数目和占用内存大小。

    jmap -dump:live,format=b,file=xxx.xxx [pid] 则可以将当前Java进程的内存占用情况导出来,方便用专门的内存分析工具(例如:MAT)来分析。

    这个命令对于分析是否有内存泄漏很有帮助。具体怎么使用可以查看本博的另一篇文章:利用Eclipse Memory Analyzer Tool(MAT)分析内存泄漏

     

    这里详细介绍下jmap -histo:live [pid] 命令:

    1

    从上图可以看出,int数组、constMethodKlass、methodKlass、constantPoolKlass都占用了大量的内存。

    特别是占用了大量内存的int数组,需要仔细检查相关代码。

     

    最后,总结下排查内存故障的方法和技巧有哪些:

    1、top命令:Linux命令。可以查看实时的内存使用情况。  

    2、jmap -histo:live [pid],然后分析具体的对象数目和占用内存大小,从而定位代码。

    3、jmap -dump:live,format=b,file=xxx.xxx [pid],然后利用MAT工具分析是否存在内存泄漏等等。

     

    1.JVM为什么可以跨平台

    JVM能跨计算机体系结构(操作系统)来执行Java字节码(JVM字节码指令集),屏蔽可与各个计算机平台相关的软件或者硬件之间的差异,使得与平台相关的耦合统一由JVM提供者来实现。

    指令集:计算机所能识别的机器语言的命令集合。

    每个运行中的Java程序都是一个JVM实例。

    2.描述JVM体系结构

    (1)类加载器:JVM启动时或者类运行时将需要的class加载到JVM中。每个被装载的类的类型对应一个Class实例,唯一表示该类,存于堆中。

    (2)执行引擎:负责执行JVM的字节码指令(CPU)。执行引擎是JVM的核心部分,作用是解析字节码指令,得到执行结果(实现方式:直接执行,JIT(just in time)即时编译转成本地代码执行,寄存器芯片模式执行,基于栈执行)。本质上就是一个个方法串起来的流程。每个Java线程就是一个执行引擎的实例,一个JVM实例中会有多个执行引擎在工作,有的执行用户程序,有的执行JVM内部程序(GC).

    (3)内存区:模拟物理机的存储、记录和调度等功能模块,如寄存器或者PC指针记录器。存储执行引擎执行时所需要存储的数据。

    (4)本地方法接口:调用操作系统本地方法返回结果。

    3.描述JVM工作机制

    机器如何执行代码:源代码-预处理器-编译器-汇编程序-目标代码-链接器-可执行程序。

    Java编译器将高级语言编译成虚拟机目标语言。

    JVM执行字节码指令是基于栈的架构,所有的操作数必须先入栈,然后根据操作码选择从栈顶弹出若干元素进行计算后将结果压入栈中。

    通过Java编译器将源代码编译成虚拟机目标语言,然后通过JVM执行引擎执行。

    4.为何JVM字节码指令选择基于栈的结构

    JVM要设计成平台无关性,很难设计统一的基于寄存器的指令。

    为了指令的紧凑性,让编译后的class文件更加紧凑,提高字节码在网络上的传输效率。

    5.描述执行引擎的架构设计

    创建新线程时,JVM会为这个线程创建一个栈,同时分配一个PC寄存器(指向第一行可执行的代码)。调用新方法时会在这个栈上创建新的栈帧数据结构。执行完成后方法对应的栈帧将消失,PC寄存器被销毁,局部变量区所有值被释放,被JVM回收。

    6. 描述javac编译器的基本结构

    Javac编译器的作用是将符合Java语言规范的的源代码转换成JVM规范的Java字节码。

    (1)词法分析器组件:找出规范化的Token流

    (2)语法分析器组件:生成符合Java语言规范的抽象语法树

    (3)语义分析器组件:将复杂的语法转化成最简单的语法,注解语法树

    (4)代码生成器组件:将语法树数据结构转化成字节码数据结构

    7.描述JVM编译优化

    早期(编译器):很少;编译时,为节省常量池空间,能确定的相同常量只用一个引用地址。

    晚期(运行期):

    方法内联:去除方法调用的成本;为其他优化建立良好基础,便于在更大范围采取连续优化的手段。

    冗余访问消除:公共子表达式消除

    复写传播:完全相等的变量可替代

    无用代码消除:清除永远不会执行的代码

       (1)公共子表达式消除(语言无关):如果公共子表达式已经计算过了,并且没有变化,那就没有必要再次计算,可用结果替换。

       (2)数组边界检查消除(语言相关):限定循环变量在取值范围之间,可节省多次条件判断。

       (3)方法内联(最重要):去除方法调用的成本;为其他优化建立良好基础,便于在更大范围采取连续优化的手段。

       (4)逃逸分析(最前沿):分析对象的动态作用域;变量作为调用参数传递到其他方法中-方法逃逸;被外部线程访问-线程逃逸。

    栈上分配-减少垃圾系统收集压力

    同步消除-如果无法逃逸出线程,则可以消除同步

    标量替换-将变量恢复原始类型来访问

    小抄:final修饰的局部变量和参数,在常量池中没有符号引用,没有访问标识,对运行期是没有任何影响的,仅仅保证其编译期间的不变性。

    8.ClassLoader(类加载器)有哪些

    (1)Bootstrap ClassLoader(启动类加载器):完全由JVM控制,加载JVM自身工作需要的类(JAVA_HOME/lib)

    (2)Extension ClassLoader(扩展类加载器):属于JVM自身一部分,不是JVM自身实现的(JAVA_HOME/lib/ext)

    (3)Appclication ClassLoader(应用程序类加载器):父类是Extension ClassLoader,加载Classpath(用户类路径)上的类库

    9.描述ClassLoader的作用(什么是类加载器)和加载过程

    将Class文件加载到JVM中、审查每个类由谁加载(父优先的等级加载机制)、将Class字节码重新解析成JVM统一要求的对象(Class对象)格式。

    .class->findclass->Liking:Class规范验证、准备、解析->类属性初始化赋值(static块的执行)->Class对象(这也就是为什么静态块只执行一次)

    10.描述JVM类加载机制

    ClassLoader首先不会自己尝试去加载类,而是把这个请求委托给父类加载器完成,每一个层次都是。只有当父加载器反馈无法完成请求时(在搜索范围内没有找到所需的类),子加载器才会尝试加载(等级加载机制、父优先、双亲委派)。

    好处:类随着它的加载器一起具有一种带有优先级的层次关系;保证同一个类只能被一个加载器加载。

    11.JVM加载class文件到内存的两种方式

    (1)隐式加载:继承或者引用的类不在内存中

    (2)显式加载:代码中通过调用ClassLoader加载

    12.加载类错误分析及其解决

    (1)ClassNotFoundException:没有找到对应的字节码(.class)文件;检查classpath下有无对应文件

    (2)NoClassDefFoundError:隐式加载时没有找到,ClassNotFoundException引发NoClassDefFoundError;确保每个类引用的类都在classpath下

    (3)UnsatisfiedLinkError(未满足链接错误):删除了JVM的某个lib文件或者解析native标识的方法时找不到对应的本地库文件

    (4)ClassCastException:强制类型转换时出现这个错误;容器类型最好显示指明其所包含对象类型、先instanceof检查是不是目标类型,再类型转换

    (5)ExceptionInitializerError:给类的静态属性赋值时

    13:Java应不应该动态加载类(JVM能不能动态加载类)

    JVM中对象只有一份,不能被替换,对象的引用关系只有对象的创建者持有和使用,JVM不可干预对象的引用关系,因为JVM不知道对象是怎么被使用的,JVM不知道对象的运行时类型,只知道编译时类型。

    但是可以不保存对象的状态,对象创建和使用后就被释放掉,下次修改后,对象就是新的了(JSP)。

    14.Java中哪些组件需要使用内存

    (1)Java堆:存储Java对象

    (2)线程:Java运行程序的实体

    (3)类和类加载器:存储在堆中,这部分区域叫永久代(PermGen区)

    (4)NIO:基于通道和缓冲区来执行I/O的新方式。

    (5)JNI:本地代码可以调用Java方法,Java方法也可以调用本地代码

    15.描述JVM内存结构及内存溢出。

    JVM是按照运行时数据的存储结构来划分内存结构的。

    PC寄存器数据:严格来说是一个数据结构,保存当前正在执行的程序的内存地址。为了线程切换后能恢复到正确的执行位置,线程私有。

    不会内存溢出。

    (1)Java栈:方法执行的内存模型,存储线程执行所需要的数据。线程私有。
    --OutOfMemoryError:JVM扩展栈时无法申请到足够的空间。一个不断调用自身而不会终止的方法。

    --StackOverflowError:请求的栈深度大于JVM所允许的栈深度。创建足够多的线程。

    (2)堆:存储对象,每一个存在堆中Java对象都是这个对象的类的副本,复制包括继承自他父类的所有非静态属性。线程共享。

    --OutOfMemoryError:对象数量到达堆容量限制。可通过不断向ArrayList中添加对象实现。

    (3)方法区:存储类结构信息。包括常量池(编译期生产的各种字面量和符号引用)和运行时常量池。线程共享。

    --OutOfMemoryError:同运行时常量池。

    (4)本地方法栈:与Java栈类似,为JVM运行Native方法准备的空间。线程私有。(C栈)OutOfMemoryError和StackOverflowError同JVM栈。

    (5)运行时常量池:代表运行时每个class文件中的常量表。运行期间产生的新的常量放入运行时常量池。

    --OutOfMemoryError:不断向List<String>中添加字符串,然后String.inern(),PermGen Space(运行时常量池属于方法区)。

    (6)本地直接内存:即NIO。

    --OutOfMemoryError:通过直接向操作系统申请分配内存。

    16.描述JVM内存分配策略

     

    (1)对象优先分配在Eden

    (2)大对象直接进入老年代

    (3)长期存活的对象将进入老年代

    (4)幸存区相同年龄对象的占幸存区空间的多于其一半,将进入老年代

    (5)空间担保分配(老年代剩余空间需多于幸存区的一半,否则要Full GC)

    17.描述JVM如何检测垃圾

    通过可达性分析算法,通过一些列称为GC Roots的对象作为起始点,从这些起始点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(GC Roots到这个对象不可达),则证明这个对象是不可用的。

    使用可达性分析算法而不是引用计数算法。因为引用计数算法很难解决对象之间相互循环引用的问题。

    18.哪些元素可作为GC Roots

     

    (1)JVM栈(栈帧中的本地变量表)中的引用

    (2)方法区中类静态属性引用

    (3)方法区中常量引用

    (4)本地方法栈中JNI(一般的Native方法)引用

    19.描述分代垃圾收集算法的思路:

    把对象按照寿命长短来分组,分为年轻代和老年代,新创建的在老年代,经历几次回收后仍然存活的对象进入老年代,老年代的垃圾频率不像年轻代那样频繁,减少每次收集都去扫描所有对象的数量,提高垃圾回收效率。

    20.描述基于分代的堆结构及其比例。

    (1)年轻代(Young区-1/4):Eden+Survior(1/8,这个比例保证只有10%的空间被浪费,保证每次回收都只有不多于10%的对象存活)=From+To,存放新创建的对象.

    (2)老年代(Old区 ):存放几次垃圾收集后存活的对象

    (3)永久区(Perm区):存放类的Class对象

    21.描述垃圾收集算法

     

    (1)标记-清除算法:首先标记处所要回收的对象,标记完成后统一清除。缺点:标记效率低,清除效率低,回收结束后会产生大量不连续的内存碎片(没有足够连续空间分配内存,提前触发另一次垃圾回收)。适用于对象存活率高的老年代。

     

    (2)复制算法(Survivor的from和to区,from和to会互换角色):

    将内存容量划分大小相等的两块,每次只使用其中一块。一块用完,就将存活的对象复制到另一块,然后把使用过的一块一次清除。不用考虑内存碎片,每次只要移动顶端指针,按顺序分配内存即可,实现简单运行高效。适用于新生代。

    缺点:内存缩小为原来的一般,代价高。浪费50%的空间。

    (3)标记-整理算法:
    标记完成后,将存活的对象移动到一端,然后清除边界以外的内存。适用于对象存活率高的老年代。

    22.描述新生代和老年代的回收策略

    Eden区满后触发minor GC,将所有存活对象复制到一个Survivor区,另一个Survivor区存活的对象也复制到这个Survivor区中,始终保证有一个Survivor是空的。

    Toung区Survivor满后触发minor GC后仍然存活的对象存到Old区,如果Survivor区放不下Eden区的对象或者Survivor区对象足够老了,直接放入Old区,如果Old区放不下则触发Full GC。

    Perm区满将触发Major GC。

    22.描述CMS垃圾收集器

     

    CMS 收集器:Concurrent Mark Sweep 并发标记-清除。重视响应速度,适用于互联网和B/S系统的服务端上。初始标记还是需要Stop the world 但是速度很快。缺点:CPU资源敏感,无法浮动处理垃圾,会有大量空间碎片产生。

    补充:

    1.Java应不应该动态记载类

    Java的优势正是基于共享对象的机制,达到信息的高度共享,也就是通过保存并持有对象的状态而省去类信息的重复创建和回收。对象一旦被创建,就可以被人持有和利用。动态加载理论上可以直接替换这个对象,然后更新Java栈中所有对原对象的引用关系,但是仍然不可行,因为这违反了JVM的设计原则,对象的引用只有对象的创建者持有和使用,JVM并不可以干预对象的引用关系,因为JVM并不知道对象是怎么办使用的,JVM并不知道对象的运行时类型而只知道编译时类型。

    假如一个对象的属性结构被修改,但在运行时其他对象可能仍然引用该属性。

    但是可以采取不保存对象的状态的解决办法,对象被创建后使用后就被释放掉,下次修改后,对象也就是新的了。JSP和其他解释型语言都是如此。

     

     

  • 相关阅读:
    BZOJ3456: 城市规划
    BZOJ4555: [Tjoi2016&Heoi2016]求和
    关于wqs二分(凸优化) 实数二分和整数二分的一些讨论
    求逆矩阵模板
    再探模拟费用流一类问题
    「九省联考 2018」秘密袭击(差分+生成函数+拉格朗日插值+整体dp+线段树合并):
    「九省联考 2018」IIIDX(贪心+线段树(平衡树)):
    【GDOI2020模拟03.28】数二数(two) (计数动态规划):
    「十二省联考 2019」骗分过样例(提答+数论+乱搞):
    二进制分组学习小记:
  • 原文地址:https://www.cnblogs.com/xuwc/p/8727488.html
Copyright © 2011-2022 走看看