JVM相关
JVM相关
打印GC相关的信息并分析
JVM提供了大量命令行参数,打印信息,供调试使用.主要有以下一些:
java -verbose:gc hash 或 java -XX:+PrintGC hash
输出形式:
[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]
以其中一行为例来解读下日志信息:
[GC (Allocation Failure) [ParNew: 367523K->1293K(410432K), 0.0023988 secs] 522739K->156516K(1322496K), 0.0025301 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
- GC:
表明进行了一次垃圾回收,前面没有Full修饰,表明这是一次Minor GC ,注意它不表示只GC新生代,并且现有的不管是新生代还是老年代都会STW。 - Allocation Failure:
表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。 - ParNew:
表明本次GC发生在年轻代并且使用的是ParNew垃圾收集器。ParNew是一个Serial收集器的多线程版本,会使用多个CPU和线程完成垃圾收集工作(默认使用的线程数和CPU数相同,可以使用-XX:ParallelGCThreads参数限制)。该收集器采用复制算法回收内存,期间会停止其他工作线程,即Stop The World。 - 367523K->1293K(410432K):
单位是KB
三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。
- 0.0023988 secs:
该内存区域GC耗时,单位是秒 - 522739K->156516K(1322496K):
三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。 - 0.0025301 secs:
该内存区域GC耗时,单位是秒 - [Times: user=0.04 sys=0.00, real=0.01 secs]:
分别表示用户态耗时,内核态耗时和总耗时
分析下可以得出结论:
该次GC新生代减少了367523-1293=366239K
Heap区总共减少了522739-156516=366223K
366239 – 366223 =16K,说明该次共有16K内存从年轻代移到了老年代,可以看出来数量并不多,说明都是生命周期短的对象,只是这种对象有很多。
我们需要的是尽量避免Full GC的发生,让对象尽可能的在年轻代就回收掉,所以这里可以稍微增加一点年轻代的大小,让那16K的数据也保存在年轻代中。
GC时,用什么方法判断哪些对象是需要回收:
- 引用计数法(已经不用了)
简而言之就是给对象添加一个引用计数器,有其他地方引用时这个计数器+1,引用失效时-1,为0时就可以删除掉了。但是它不能解决循环引用的问题,所以一般使用的都是后一种算法。 - 可达性分析法
可达性分析法的基本思路就是通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那就可以回收掉了。
GC Roots一般都是些堆外指向堆内的引用,例如:
JVM栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象
参考链接:https://www.cnblogs.com/jpfss/p/8618297.html
java-xx参数介绍及调优总结
https://blog.csdn.net/weixin_39407066/article/details/84582380
实战:通过gc比较String.replace性能差异
- 代码 true
public class TestStringReplace { private static String replace(String src, String pattern, String replacement) { int i = src.indexOf(pattern); if (i >= 0) { int ptnLen = pattern.length(); StringBuilder sb = new StringBuilder(src.length()); sb.append(src, 0, i).append(replacement); i += ptnLen; for (;;) { int j = src.indexOf(pattern, i); if (j >= 0) { sb.append(src, i, j).append(replacement); i = j + ptnLen; } else { return sb.append(src, i, src.length()).toString(); } } } else { return src; } } private static final int LOOPS = 100 * 10000; private static final String SRC = "asdfasdfassdd2edsdfasdfasdfasdfasdfasdfasdsdfasdfasdfasdfasdfasdfasdsdfasdfasdfasdfsd"; private static final String PATTERN = "sdf"; private static final String REP = "ijkk2eeew;"; public static void main(String[] args) { System.out.println(replace(SRC, PATTERN, REP)); System.out.println(replace(SRC, PATTERN, REP).equals(SRC.replace(PATTERN, REP))); System.out.println("test replace1---------------------------------------"); timeit(() -> { for (int i = 0; i < LOOPS; i++) { replace(SRC, PATTERN, REP); } }, 6); System.out.println("test string.replace-------------------------------------"); timeit(() -> { for (int i = 0; i < LOOPS; i++) { SRC.replace(PATTERN, REP); } }, 6); } private static void timeit(Runnable r, int times) { long total = 0; for (int i = 0; i < times; i++) { long start = System.currentTimeMillis(); r.run(); long end = System.currentTimeMillis(); total += end - start; System.out.println(end - start); } System.out.println("AVG " + total / times); } } ``` 2. 执行命令运行TestStringReplace time java -XX:+UseG1GC -Xmx4G -Xms4G -XX:+PrintGC TestStringReplace
test replace1—————————————
[GC pause (G1 Evacuation Pause) (young) 204M->608K(4096M), 0.0016481 secs]
[GC pause (G1 Evacuation Pause) (young) 236M->709K(4096M), 0.0017248 secs]
1333
[GC pause (G1 Evacuation Pause) (young) 2454M->750K(4096M), 0.0036411 secs]
1239
688
[GC pause (G1 Evacuation Pause) (young) 2454M->684K(4096M), 0.0019600 secs]
593
[GC pause (G1 Evacuation Pause) (young) 2454M->666K(4096M), 0.0023456 secs]
579
572
AVG 834
test string.replace————————————-
[GC pause (G1 Evacuation Pause) (young) 2454M->662K(4096M), 0.0020937 secs]
[GC pause (G1 Evacuation Pause) (young) 2454M->689K(4096M), 0.0025844 secs]
1857
[GC pause (G1 Evacuation Pause) (young) 2454M->729K(4096M), 0.0022971 secs]
1789
[GC pause (G1 Evacuation Pause) (young) 2454M->708K(4096M), 0.0021608 secs]
1792
[GC pause (G1 Evacuation Pause) (young) 2454M->710K(4096M), 0.0023275 secs]
1781
[GC pause (G1 Evacuation Pause) (young) 2454M->661K(4096M), 0.0022797 secs]
1809
[GC pause (G1 Evacuation Pause) (young) 2454M->662K(4096M), 0.0020625 secs]
[GC pause (G1 Evacuation Pause) (young) 2454M->778K(4096M), 0.0023225 secs]
1787
AVG 1802
java -XX:+UseG1GC -Xmx4G -Xms4G -XX:+PrintGC TestStringReplace 15.63s user 1.02s system 102% cpu 16.233 total总结:主要是节省了官方实现通过Regex这一步,虽然性能差距不大,但是这并非是对性能的吹毛求疵,而是一眼看过去的直觉就感觉不是很舒服。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!