├── JVM内存分配.md ├── JVM垃圾回收.md ├── JVM性能查看工具.md ├── README.md └── java进程高cpu使用率排查.md /JVM内存分配.md: -------------------------------------------------------------------------------- 1 | #JVM内存分配 2 | ## 1.JVM内存分配 3 | ###1.1 栈内存分配 4 | - 保存参数、局部变量、中间计算过程和其他数据。退出方法的时候,修改栈顶指针就可以把栈帧的内容销毁 5 | - 栈的优点:存取速度比堆快,仅次于寄存器,栈数据可以共享 6 | - 栈的缺点:存在栈中的数据大小、生存周期是在编译的时候指定的,导致其缺乏灵活性 7 | 8 | 9 | ###1.1 堆内存分配 10 | 11 | - 堆的优点:动态地分配内存大小,生存期不必事先告诉编辑器,它是在运行期动态分配的,垃圾回收会自动的收走不再使用的空间区域 12 | - 堆的缺点:运行时动态分配内存,在分配和销毁时都需要占用时间,因此堆的效率比较低 13 | 14 | ## 2.JVM堆结构 15 | 16 | 年轻代(Eden Survivor(from + to) ) 17 | 老年代() 18 | metadata() 19 | ### 为什么java这么分区? 20 | 21 | ## 3.JVM堆结构和垃圾回收 22 | stop the world... 23 | 垃圾回收过程中是不会工作的~ 24 | 25 | ## 4.JVM堆配置参数 26 | 27 | - -Xms 初始堆大小 28 | 29 | 默认物理内存的1/64(<1G) 30 | - -Xmx 最大堆大小 31 | 32 | 默认物理内存的1/4(<1G),实际中建议不大于4GB 33 | - 一般建议设置 -Xms= -Xmx 34 | 35 | 好处是避免每次gc后,调整堆的大小,避免动态伸缩,减少系统的内存分配开销 36 | 37 | 内存泄露和内存溢出: 38 | 内存泄露也有可能是代码问题,也有可能是物理内存大小; 39 | 40 | - 整个堆的大小=年轻代大小+年老代大小+metadata 41 | 42 | ## 4.JVM新生代(young generation) 43 | 44 | - 1、新生代=1个eden区+2个survivor区 45 | - 2、-Xmn 新生代大小(1.4版本或以后~) 46 | 47 | 默认值大小为整个堆的3/8 48 | - 3、 -XX:NewRatio 49 | 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去代) 50 | Xms=Xmx并且设置了Xmn的情况下,该参数不需要设置 51 | 52 | - 4、-XX:SurvivorRatio 53 | 54 | E区和Survivor区的大小比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10 55 | 56 | - 5、 作用:用来存储JVM新分配的java对象 57 | 58 | ## 5.JVM老年代(tenured generation) 59 | 60 | - 1、老年代=整个堆-年轻代大小-持久代大小 61 | - 2、年轻代中经过垃圾回收没有被回收掉的对象,被复制到老年代 62 | - 3、老年代存储对象比年轻代年龄大的多,而且不乏大对象 63 | - 4、新建的对象也有可能直接进入老年代 64 | 65 | 1.大对象,可通过启动设置参数-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配 66 | 2.大的数组对象,且数组中无引用外部对象 67 | - 5、老年代大小无配置参数 68 | 69 | 70 | ## 6.JVM持久代(perm generation) 71 | - 1、持久代=整个堆-年轻代大小-年老代大小 72 | - 2、-XX:PermSize -XX:MaxPermSize 73 | 74 | 设置持久代的大小,一般情况推荐把-XX:PermSize设置成-XX:MaxPermSize的值为相同的值。因为永久代大小的调整会导致内存需要触发fullgc 75 | 76 | - 3、存放class、method元信息,其大小与项目的规模、类、方法的数量有关。一般设置为128M就足够,设置原则是预留空间的30% 77 | - 4、持久代的回收方式 78 | 1.常量池中的常量,无用的类信息,常量的回收很简单,没有引用了就可以被回收 79 | 2.对于无用的类进行回收,必须保证3点: 80 | 类的所有实例都已经被回收 81 | 加载类的classloader已经被回收 82 | 类对象的class对象没有被引用(即没有提供反射引用该类的地方) 83 | -------------------------------------------------------------------------------- /JVM垃圾回收.md: -------------------------------------------------------------------------------- 1 | #JVM内存垃圾回收 2 | 3 | ##1.jvm垃圾收集算法 4 | 5 | - 1.引用计算算法(jdk1.2之前) 6 | 7 | 每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。还有一个问题是如何解决精准计数。 8 | - 2.根搜索算法 9 | 10 | 从GC Roots开始向下搜索 11 | ,搜索所走过的路径成为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象 12 | 13 | 在java语言中,GC Roots包括: 14 | 虚拟机栈中引用的对象(最常用) 15 | 方法区中类静态熟性实体引用的对象(很少用) 16 | 方法区中常量引用的对象(很少用) 17 | 本地方法栈中JNI引用的对象 18 | 19 | ##2.jvm垃圾回收算法 20 | 21 | - 1、复制算法(copying) 22 | 23 | 1.复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间,用于进行对象的移动 24 | 2.此算法用于新生代内存回收,从E区回收到S0或者S1 25 | - 2、标记清除算法(Mark-Sweep) 26 | 27 | 1.标记-清除算法采用从根集合进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如何所示; 28 | 2.标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效(使用于老年代),但由于标记-清除算法直接回收不存活的对象,因 29 | 30 | 此会造成内存碎片! 31 | - 3、标记整理算法(Mark-Compac) 32 | 33 | 标记-整理算法采用标记-清除算法一样的方式进行标记,但是清除的时候不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法在标记-清除算法的基础上,又进行了对象的移动,因此成本很高,但却解决了内存碎片的问题。 34 | 35 | ##3.相关的名词解释 36 | 37 | - 1 串行回收 38 | 39 | gc单线程内存回收,会暂停所有用户线程 40 | 41 | - 2 并行回收 42 | 43 | 收集是指多个GC线程并行工作,但此时用户线程是暂停的;所以serial是串行的,parallel收集器是并行的,而CMS收集器是并发的 44 | 45 | - 3 并发回收 46 | 47 | 是指用户线程与GC线程是同时执行(不一定是并行,可能交替,但总体是在同时执行的),不需要停顿用户线程(其实在CMS中用户线程还是需要停顿的,只是非常短,GC线程在另一个CPU上执行) 48 | 49 | ##3.1 serial回收器(串行回收器) 50 | 51 | 1.是一个单线程的收集器,只能使用一个CPU或一条线程去完成垃圾收集;在进行垃圾收集时,必须暂停所有其他的工作线程,直到收集完成。 52 | 2.缺点:stop the world 53 | 3.优势:简单。但对于CPU的情况,由于没有多线程的互相开销,反而可以更高效,是client模式下默认的新生代收集器 54 | 55 | 新生代Serial回收器 56 | 1.-XX:+UseSerialGC来开启 57 | Serial New + Serial Old的收集器组合进行内存回收 58 | 2.使用复制算法 59 | 3.独占式的垃圾回收 60 | 一个线程进行GC、串行。其他的工作线程暂停 61 | 62 | 老年代Serial回收器 63 | 1.-XX:+UseSerialGC来开启 64 | Serial New + Serial Old的收集器组合进行内存回收 65 | 2.使用标记-压缩算法 66 | 3.串行的、独占式的垃圾回收器 67 | 因为内存比较大的原因,回收比新生代慢 68 | 69 | ##3.2 ParNew 回收器(并行回收器) 70 | 并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差 71 | 72 | 新生代ParNew回收器 73 | 1.-XX:+UseParNewGC开启 74 | 新生代使用并行回收收集器,老年代使用串行收集器 75 | 2.-XX:ParallelGCThreads指导线程数 76 | 默认最好与CPU数量相当,避免过多的线程数影响垃圾回性能 77 | 3.使用复制算法 78 | 4.并行的、独占式的垃圾回收器 79 | 80 | 新生代ParNew Scavenge回收器 81 | 1.吞吐量优先回收器 82 | 关注CPU吞吐量,即运行用户代码的时间/总时间,比如:JVM运行100分钟,其中运行用户代码99分钟,垃圾回收1分钟,则吞吐量99%,这种收集器能最高效率的利用CPU,适合后台运算 83 | 2.-XX:+UseParallelGC开启 84 | 使用Parallel Scavenge+Serial Old收集器组合回收垃圾,这也是在Server模式下的默认值 85 | 3.-XX:GCTimeRatio 86 | 来设置用户执行时间占总时间的比例,默认是99,即1%的时间用来垃圾回收 87 | 4.-XX:MaxGCPauseMilis 88 | 设置GC最大的停顿时间 89 | 5.使用复制算法 90 | 91 | 老生代ParNew Old回收器 92 | 1.-XX:+UseParallelOldGC开启 93 | 使用Parallel Scavenge+Serial Old组合收集器进行收集 94 | 2.使用标记整理算法 95 | 3.并行的、独占时的垃圾回收器 96 | 97 | ##3.3 CMS 回收器(并发标记清除) 98 | 99 | CMS(并发标记清除)回收器 100 | 运行过程分为4个阶段: 101 | 初始阶段(CMS initial mark):值标记GC Roots能直接关联的对象 102 | 并发标记(CMS concurrent mark):进行 GC Roots Tracing的过程 103 | 重新标记(CMS remark):修正并发标记期间因用户继续运行而导致标记发生改变的那一部分对象的标记 104 | 并发清除(CMS concurrent sweep): 105 | 期中标记和重新标记两个阶段仍然需要stop-the-world,整个过程中耗时最长的并发标记和并发清除过程中收集器都可以和用户线程一起工作 106 | 107 | - 1、标记-清除算法 108 | 109 | 同时它是一个使用多线程并发回收的垃圾回收器 110 | - 2、-XX:ParallelCMSThreads 111 | 112 | 手工设定CMS的线程数量,CMS默认启动的线程数是(ParallelGCThreads+3)/4 113 | - 3、-XX:+UseConcMarkSweepGC开启 114 | 115 | 使用ParNew+CMS+ Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现"Concurrent Mode Failure"失败后的后备收集器使用 116 | - 4、-XX:CMSInitiatingOccupancyFration 117 | 118 | 设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值是68%,仅在CMS收集器有效,-XX:CMSInitiatingOccupancyFration=70 119 | - 5、-XX:+UseCMSCompactAtFullCollection 120 | 121 | 由于CMS收集器会产生碎片,此参数设置在垃圾回收器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效。 122 | - 6、-XX:+CMSFullGCBeforeCompaction 123 | 124 | 设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用 125 | - 7、-XX:CMSInitiatingPermOccupancyFration(你不建议设置) 126 | 127 | 设置Perm Gen使用到达多少比率时触发,默认92s 128 | 129 | 130 | ##3.4 GC性能指标 131 | 132 | - 吞吐量 应用花在非GC上的时间百分比 133 | - GC负荷 与吞吐量相反,指应用花在GC上的时间比 134 | - 暂停时间 应用花在GC stop-the-world的时间 135 | - GC频率 顾名思义 136 | - 反应速度 从一个对象变成垃圾到这个对象被回收的时间 137 | - 一个交互式的应用要求暂停时间越少越好,然而,一个非交互性的应用,当然是希望GC负荷越低越好 138 | - 一个实时系统对暂停时间和GC负荷的要求,都是越低越好 139 | 140 | 141 | ##4.内存容量配置原则 142 | 143 | ###4.1 年轻代大小选择 144 | 145 | 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间设置(根据实际情况选择),在此种情况下,年轻代收集发送的频率也是最小的。同时,减少到达年老代的对象 146 | 147 | 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度,因为对响应时间没有要求,垃圾回收可以并行进行,一般适合8CPU以上的应用 148 | 149 | 避免设置过小,当新声代设置过小时会导致:1YGC次数更加频繁 2.可能导致YGC对象直接进入旧生代,会出发fullGC 150 | 151 | ###4.2 年老代大小选择 152 | 153 | 响应时间优先的应用:年轻代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话频率和会话持续时间等一些参数,如果堆设置小了。可以造成会话碎片,高回收频率以及应用暂停而使用传统的标记清除的方式;如果堆大了,则需要较长的收集时间,最优化的方案,一般需要参考一下数据获得: 154 | 155 | 并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收的时间比例 156 | 吞吐量优先的应用:一般吞吐量的应用都有一个很大的年轻代和一个较小的老年代。原因是:这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活的对象 157 | -------------------------------------------------------------------------------- /JVM性能查看工具.md: -------------------------------------------------------------------------------- 1 | #JVM性能查看工具 2 | ##1.jstat 3 | 作用:利用jvm内建的指令对java应用程序的资源和性能进行实时的命令行监控,包括针对heap size和垃圾回收情况的监控。 4 | 用法: jstat [option] pid 5 | option: 6 | -class:统计class loader行为信息 7 | -compile:统计编译行为信息 8 | -gc:统计jdk gc时heap信息 9 | -gccapacity:统计不同的generations(包括新生区,老年区,permanent区)相应的heap容量情况 10 | -gccause:统计gc的情况,(同-gcutil)和引起gc的事件 11 | -gcnew:统计gc时,新生代的情况 12 | -gcnewcapacity:统计gc时,新生代heap容量 13 | -gcold:统计gc时,老年区的情况 14 | -gcoldcapacity:统计gc时,老年区heap容量 15 | -gcpermcapacity:统计gc时,permanent区heap容量 16 | -gcutil:统计gc时,heap情况 17 | ### -gc 18 | [root@wangxu-180 bin]# jstat -gc 21371 1000 2 #<=间隔为1s,重复出现两次~ 19 | S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 20 | 10240.0 10240.0 9303.5 0.0 81920.0 45348.6 159744.0 56841.3 37888.0 36902.9 4608.0 4349.1 14 0.735 1 0.004 0.740 21 | 10240.0 10240.0 9303.5 0.0 81920.0 45348.6 159744.0 56841.3 37888.0 36902.9 4608.0 4349.1 14 0.735 1 0.004 0.740 22 | 参数含义: 23 | S0C:年轻代的第一个survicor(幸存区)的容量(字节) 24 | S1C:年轻代的第二个survicor(幸存区)的容量(字节) 25 | S0U:年轻代的第一个survicor(幸存区)已经使用的空间(字节) 26 | S1U:年轻代的第二个survicor(幸存区)已经使用的空间(字节) 27 | EC:年轻代中的Eden(伊甸园)的容量 28 | EU:年轻代中的Een(伊甸园)已经使用的容量 29 | OC:年老代的容量 30 | OU:年老代已经使用的空间 31 | MC:metadata的容量 32 | MU:metadata已经使用的空间 33 | CCSC:压缩类空间的大小 34 | CCSU:压缩类空间的使用大小 35 | YG:从应用程序启动到采样时年轻代中gc次数 36 | YGCT:从应用程序启动到采样时年轻代中gc所用时间(s) 37 | FGC:从应用程序启动到采样时年老代gc次数 38 | FGCT:从应用程序启动到采样时年老代(全gc)gc所用时间(s) 39 | GC: 从应用程序启动到采样时gc用的总时间(s) 40 | 41 | 42 | 43 | 44 | 45 | 46 | ##2.jmap 47 | 作用:查看jvm的内存分配情况 48 | 用法:jmap [option] pid 49 | - heap:打印堆的内存信息 50 | - dump: 51 | 52 | [root@iauth02 java_bin]# jmap -heap 12249 53 | Attaching to process ID 12249, please wait... 54 | Debugger attached successfully. 55 | Server compiler detected. 56 | JVM version is 25.77-b03 57 | 58 | using thread-local object allocation. 59 | Parallel GC with 2 thread(s) 60 | 61 | Heap Configuration: #堆内存初始化设置 62 | MinHeapFreeRatio = 0 #<= -XX:MinHeapFreeRatio=设置JVM堆最小空闲比率 63 | MaxHeapFreeRatio = 100 #<= -XX:MaxHeapFreeRatio=设置JVM堆最大空闲比率 64 | MaxHeapSize = 1006632960 (960.0MB) #<=-XX:MaxHeapSize=JVM堆最大内存大小 65 | NewSize = 20971520 (20.0MB) #<= -XX:NewSize=设置JVM堆新生代内存的默认大小 66 | MaxNewSize = 335544320 (320.0MB) #<= -XX:MaxNewSize=设置JVM堆新生代内存的最大大小 67 | OldSize = 41943040 (40.0MB) #<= -XX:OldSize=设置JVM堆的老年代大小 68 | NewRatio = 2 #<= -XX:NewRatio=:新生代和老生代的大小比率 69 | SurvivorRatio = 8 #<= -XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 70 | MetaspaceSize = 21807104 (20.796875MB) #<= -XX:MetaspaceSize= 设置元空间的初始大小 71 | CompressedClassSpaceSize = 1073741824 (1024.0MB) 72 | MaxMetaspaceSize = 17592186044415 MB #<= -XX:MetaspaceSize= 设置元空间的最大大小 73 | G1HeapRegionSize = 0 (0.0MB) 74 | 75 | Heap Usage: 76 | PS Young Generation 77 | Eden Space: #<=eden区内存分布 78 | capacity = 202375168 (193.0MB) 79 | used = 164582424 (156.9580307006836MB) 80 | free = 37792744 (36.041969299316406MB) 81 | 81.32540450812621% used 82 | From Space: #<=survivor0区内存分布 83 | capacity = 2097152 (2.0MB) 84 | used = 1676768 (1.599090576171875MB) 85 | free = 420384 (0.400909423828125MB) 86 | 79.95452880859375% used 87 | To Space: #<=survivor1区内存分布 88 | capacity = 2097152 (2.0MB) 89 | used = 0 (0.0MB) 90 | free = 2097152 (2.0MB) 91 | 0.0% used 92 | PS Old Generation #<=老生代区内存分布 93 | capacity = 186646528 (178.0MB) 94 | used = 112483992 (107.2730941772461MB) 95 | free = 74162536 (70.7269058227539MB) 96 | 60.265783245643874% used 97 | 98 | 30512 interned Strings occupying 3600128 bytes. 99 | 100 | 101 | 102 | ##3.jstack 103 | 作用:查看java程序的线程运行情况 104 | 用法:jstack [选项] pid 105 | 常用:jstack pid 106 | 注意:如果java程序是在tomcat用户下启动的,一定必须在对应的tomcat用户下执行该命令 107 | 108 | [root@wangxu-180 bin]# jps 109 | 7879 Jps 110 | 21371 111 | [root@wangxu-180 bin]# su - tomcat --shell=/bin/bash -c 'jstack 21371' 2>/dev/null >/home/wangxu/java.txt 112 | 113 | 114 | 115 | ##4.jps 116 | 查看jvm进程的pid 117 | 118 | 119 | ##5.jinfo 120 | [root@iauth01 wangxu]# jps 121 | 9810 122 | 11400 Jps 123 | [root@iauth01 wangxu]# jinfo -flags 9810 124 | Attaching to process ID 9810, please wait... 125 | Debugger attached successfully. 126 | Server compiler detected. 127 | JVM version is 25.77-b03 128 | Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=1006632960 -XX:MaxHeapSize=1006632960 -XX:MaxMetaspaceSize=230686720 -XX:MaxNewSize=335544320 -XX:MetaspaceSize=230686720 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=335544320 -XX:OldSize=671088640 -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 129 | Command line: -Djava.util.logging.config.file=/usr/local/apache-tomcat-7.0.73/conf/logging.properties -Xmx960m -Xms960m -XX:NewSize=320m -XX:MaxNewSize=320m -XX:SurvivorRatio=8 -XX:MetaspaceSize=220m -XX:MaxMetaspaceSize=220m -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -javaagent:/usr/local/apache-tomcat-7.0.73/tingyun/tingyun-agent-java.jar -Djava.endorsed.dirs= -Dcatalina.base=/usr/local/apache-tomcat-7.0.73 -Dcatalina.home=/usr/local/apache-tomcat-7.0.73 -Djava.io.tmpdir=/usr/local/apache-tomcat-7.0.73/temp -Dcommons.daemon.process.id=9810 -Dcommons.daemon.process.parent=9809 -Dcommons.daemon.version=1.0.15-dev abort 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jvm 2 | jvm堆内存结构、垃圾回收原理、jvm内存调优、jvm监控等 3 | -------------------------------------------------------------------------------- /java进程高cpu使用率排查.md: -------------------------------------------------------------------------------- 1 | #Java进程CPU使用率高排查 2 | 在生产java应用,CPU使用率一直很高,经常达到100%。让开发束手无策,最后经常是运维背锅,其实,有可能是开发代码逻辑有问题,甚至写了死循环~ 3 | 4 | ##排查思路: 5 | - 1 jps获取java进程的pid 6 | - 2 jstack pid >>java.txt 导出CPU占用高进程的线程栈 7 | - 3 top -H -p pid 查看对应的进程的哪个线程占用的CPU较高 8 | - 4 echo "obase=16;PID"|bc 将线程的PID转换为16进制 9 | - 5 在第二步导出的java.txt中查找转换为16进制的线程PID。找到对应的线程栈 10 | - 6 分析负载高的线程栈都是什么业务操作。优化程序并处理问题 --------------------------------------------------------------------------------