首页 > 代码库 > 002_JVM内存结构及调优学习

002_JVM内存结构及调优学习

1. 常遇到关于内存溢出的错误
        java.lang包下
        StackOverflowError 很少
        OutOfMemoryError: heap space(堆空间) 比较常见
        OutOfMemoryError: PermGen space 经常出现
       

2. Java虚拟机结构和属性
        
        内存区域: 保存java类和对象的物理区域
        
        堆: Java的内存区域叫做堆(heap)
        
        堆被分成3个区域:
            新域(young generation)、旧域(tenured generation)、永久域(perm generation)
            标记为virtual的部分被保留下来,必要时才分配出去。
            
            新域: 有Eden和两个救助空间survivor组成,新对象存放在Eden中
            
            旧域: 对象在两个救助空间survivor之间移动,当它们足够"老",能够被移入到保存生存期较长对象的旧域
            
            永久域: 在虚拟机的整个生存期都生存的对象
            
        常用虚拟机配置选项属性
            
            -Xmx    Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定;

            -Xms    Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;

            -Xmn    Java Heap Young区大小,不熟悉最好保留默认值;

            -Xss    每个线程的Stack大小,不熟悉最好保留默认值;
            
            具体实施查看 remark001_几招搞定JVM内存设置.txt
       

3. JVM垃圾回收机制,2种回收方法,7种垃圾收集器
        
        2种回收方法
            引用计数: 当应用程序引用某一对象的时候,JVM增加引用数,当引用超出范围时,JVM减少引用数,
                            当某对象的引用数为0时,便可以进行垃圾回收机制。(早期JVM的技术)
                
            对象引用遍历(对象引用树) => 目前大多数JVM技术
                    对象引用遍历树,是将对象引用关系构建成一棵树,从一组根对象开始,沿着整个对象树上的每条链接,
                        递归确定可到达(reachable)的对象。如果某对象别的对象(至少一个)引用,则将它作为垃圾收集起来。
                    在对象遍历阶段,GC必须记住哪些对象可以到大,以便删除不可到达的对象,这称为标记(marking)对象。
                    
        7种垃圾收集器
            标记-清除收集器(Serial收集器): 对遍历对象图进行遍历,并标记可到达的对象,然后扫描对象以寻找未标记对象并释放内存。
                该收集器一般使用单线程工作并停止其它操作。
                (你妈妈在打扫房间,会叫你出去等一会)
                
            标记-清除-压缩收集器:
                与Serial收集器有相同的标记阶段。在第二阶段,把标记对象复制到堆栈的新域中以便压缩堆栈。
                这种收集器也会停止其它操作。
                
            复制收集器: 它将堆栈分为2个域,称为半空间,它每次仅使用一个半空间。
                GC运行时,JVM生成的新对象放在一个半空间中,将可到达对象复制到另一个半空间中,从而压缩堆栈。
                这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。
                
            增量收集器: 把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。
            
            分代收集器: 把堆栈分成两个或多个域,用以存放不同寿命的对象。JVM生成的新对象一般放在其中的某个域中。
                过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。
                分代收集器对不同域使用不同的算法以优化性能。
            
            并发收集器: 同时发生,与应用程序同时运行。该收集器在某一时刻一般不得不停止其它操作以完成特定任务,
                但是因为其它应用程序可进行后台操作,所以中断其它处理的实际时间大大降低。
            
            并行收集器: 使用某种传统算法,并使用多线程并行执行它们的工作。在多CPU机器上使用多线程技术可以显著提高Java应用程序的可扩展性。
            
            解释两个名词:并发和并行。
                这两个名词都是并发编程中的概念,在谈论垃圾收集器的上下文语境中,他们可以解释为:
                    并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
                    并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),
                        用户程序继续运行,而垃圾收集程序运行于另一个CPU上。
                    
4. JVM内存区域配置: 堆区域、新域与旧域、永久区域(非堆内存)、新域子空间
        
        Sun的JVM使用的是分代收集器,它把堆分成3个主要域: 新域与旧域、永久区域(也叫非堆内存分配).
            JVM生成的所有新对象放在新域、一旦对象经历了一定数量的垃圾收集循环后,便获得使用期进入旧域。
            在永久域中JVM则存储类和方法对象。
        
        Java虚拟机的配置选项有专门用于对这些域进行配置的属性
            
            配置堆区域
                堆实质上就是新域+旧域的和,它代表这2个区域的内存大小。
                设置堆的初始大小和最大大小内存 128MB
                    java -Xms128m (其中s为start )
                    java -Xmx128m (其中x为max )
                
                通常可以将初始化大小设置为最大大小,这样可以避免程序动态增加堆的大小。
                
            配置新域和旧域
                
                设置新域大小64MB
                    java -Xms256m -Xmx256m -Xmn64m  (Xmn 其中n为new)
                    
                设置新域的初始值和最大值 64MB
                    (-XX:NewSize 设置新域初始值 )
                    (-XX:MaxNewsize 设置新域最大值 )
                    java -Xms256m -Xmx256m -XX:NewSize 64m -XX:MaxNewsize=64m
                
                设置了新域的大小,旧域大小即为堆的大小减去新域的大小,不需要对旧域进行设置,JVM也没有提供对旧域设置属性。
                
            配置永久域(非堆内存)
                
                永久域默认大小为4MB,运行程序时,JVM会调整永久域大小以满足需要。
                为了避免调整,可使用-XX:PermSize设置初始值,使用-XX:MaxPermSize设置最大值
                    java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m
                    
            配置新域子空间
                
                默认状态,JVM在新域使用复制收集器,对旧域使用标记-清除-压缩收集器。
                在新域
            
        JVM内存区域配置的实战案例: remark002_eclipse内存溢出崩溃_服务器JVM崩溃.txt    
    
5. JVM性能调优
    
        调优配置参考
                
            JVM的堆大小决定了JVM花费在收集垃圾上的时间和频度。
            
            应用中建立和释放对象的速度决定了垃圾收集的频度。
            
            编程中,注意使用对象的缓存而不是建立对象。
            
            对生存时间越长,需要收集时间也越长,收集也会变慢。
            
            一次完全的垃圾收集应该不超过3-5秒。
            
            如果系统花费很多时间收集垃圾,应该减少堆大小。
            
            一般来说,你使用物理内存的80%为堆大小。
            
            对于1GB内存,单CPU的机器来说,如下的一组参考配置
            
            -Xms800m -Xmx800m  // 堆初始值和堆最大值一样
            -Xmn200m           // 新域的内存
            -XX:PermSize=128m   // 永久域初始值
            -XX:MaxPermSize=128m // 永久域最大值
            -XX:NewSize=200m   // 新域的初始值
            -XX:MaxNewSize=200m // 新域最大值
            -XX:NewRatio=3        // 设置该值后可不设置NewSize
            -XX:SurvivorRatio=4 // 设置救助区域大小
            -XX:userParNewGC   // 可用来设置并行收集
            -XX:ParallelGCThreads   // 可用来增加并行度
            -XX:UseParallelGC  // 设置后可以使用并行清除收集器
            -XX:UseAdaptiveSizePolicy // 上面一个联合使用效果更好,利用它可以自动优化新域大小和救助空间比值
            
        我们经常遇到java.lang.OutOfMemoryError. Java heap space的错误
            
            根源是JVM虚拟机默认Heap大小是64MB,可通过设置最小值和最大值来解决
                
                (1) windows中更改系统环境变量
                    加上 JAVA_OPTS=-Xm800m -Xmx800m
                
                (2) tomcat的话,可以在\bin\catalina.bat或catalina.sh加上
                    set JAVA_OPTS=-Xm800m -Xmx800m
        
        JVM调优实战
            
            heap内存设置
            
                进入JAVA_HOME/demo/jfc/SwingSet2目录
                    java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar
                        出现如下错误: Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space
                    
                    除了异常信息,会发现程序的响应速度变慢。
                    说明Heap size偏小,GC占用了更多时间,应该分配到的执行时间较少。
                    
                    设置成如下,则安然无恙:
                        
                        java -jar -Xmn4m -Xms16m -Xmx32m SwingSet2.jar
                        
                    Heap最大不要超过可用物理内存的80%,一般要将-Xms和-Xmx选项设置为相同,
                    而-Xmn为1/4的-Xmx(heap最大值)
































002_JVM内存结构及调优学习