JVM 조정 목표: 더 높은 처리량 또는 더 낮은 대기 시간을 얻으려면 더 작은 메모리 공간을 사용하십시오.
온라인에 접속하기 전에 프로그램을 테스트하거나 실행하는 동안 과도한 CPU 로드, 요청 지연, tps 감소, 심지어 메모리 누수 등 크고 작은 JVM 문제가 발생할 수 있습니다(가비지 수집에 점점 더 많은 시간이 소요됨). ) 오랫동안 가비지 수집 빈도가 점점 높아지고 각 가비지 수집에서 정리되는 가비지 데이터가 점점 줄어들고 메모리 오버플로로 인해 시스템이 충돌하므로 JVM을 조정해야 합니다. 프로그램은 정상적인 작동을 전제로 더 나은 결과를 얻을 수 있습니다. 높은 사용자 경험과 운영 효율성.
다음은 몇 가지 중요한 지표입니다.
메모리 사용량: 프로그램의 정상적인 작동에 필요한 메모리 양입니다.
대기 시간: 가비지 수집으로 인한 프로그램 일시 중지 시간입니다.
처리량: 사용자 프로그램 실행 시간과 사용자 프로그램 및 가비지 수집이 차지하는 총 시간의 비율입니다.
물론 CAP 원리와 마찬가지로 프로그램의 작은 메모리 공간, 낮은 지연 시간, 높은 처리량을 동시에 만족시키는 것은 불가능하며, 튜닝 시 고려하는 방향도 다릅니다. 또한 튜닝에 앞서 실제 시나리오와 결합하여 명확한 최적화 목표를 갖고 성능 병목 현상을 찾아 목표 방식으로 병목 현상을 최적화한 후 최종적으로 다양한 모니터링 도구를 통해 튜닝 결과가 목표를 충족하는지 테스트를 수행해야 합니다.
JVM Tuning Tool
(1) 튜닝이 의존하고 참조할 수 있는 데이터에는 시스템 실행 로그, 스택 오류 메시지, gc 로그, 스레드 스냅샷, 힙 덤프 스냅샷 등이 포함됩니다.
① 시스템 작동 로그: 시스템 작동 로그는 프로그램 코드에 인쇄된 로그로, 코드 수준에서 시스템 작동 트랙(실행 방법, 입력 매개변수, 반환 값 등)을 설명합니다. 시스템에 문제가 있는 경우, 시스템 동작 로그는 가장 먼저 살펴보아야 할 것이 로그이다.
②스택 오류 정보: 시스템에서 예외가 발생하면 스택 정보를 기반으로 초기에 문제를 찾을 수 있습니다. 예를 들어 "java.lang.OutOfMemoryError: Java heap space"를 기반으로 이를 확인할 수 있습니다. 힙 메모리 오버플로; "java.lang.StackOverflowError"에 따라 스택 오버플로로 판단할 수 있으며, "java.lang.OutOfMemoryError: PermGen space"에 따라 메소드 영역 오버플로로 판단할 수 있습니다.
3GC 로그: 프로그램 시작 시 -XX:+PrintGCDetails 및 -Xloggc:/data/jvm/gc.log를 사용합니다. 프로그램이 실행되는 동안 gc의 자세한 프로세스를 기록하거나 "-verbose: gc" 매개변수입니다. gc 로그는 콘솔에 인쇄됩니다. 기록된 gc 로그는 각 메모리 영역의 gc 빈도, 시간 등을 분석하여 문제를 찾아 목표 최적화를 수행할 수 있습니다.
예를 들어 다음 GC 로그는
2018-08-02T14:39:11.560-0800: 10.171: [GC [PSYoungGen: 30128K->4091K(30208K)] 51092K->50790K(98816K), 0.0140970 secs] [Times: user=0.02 sys=0.03, real=0.01 secs] 2018-08-02T14:39:11.574-0800: 10.185: [Full GC [PSYoungGen: 4091K->0K(30208K)] [ParOldGen: 46698K->50669K(68608K)] 50790K->50669K(98816K) [PSPermGen: 2635K->2634K(21504K)], 0.0160030 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 2018-08-02T14:39:14.045-0800: 12.656: [GC [PSYoungGen: 14097K->4064K(30208K)] 64766K->64536K(98816K), 0.0117690 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 2018-08-02T14:39:14.057-0800: 12.668: [Full GC [PSYoungGen: 4064K->0K(30208K)] [ParOldGen: 60471K->401K(68608K)] 64536K->401K(98816K) [PSPermGen: 2634K->2634K(21504K)], 0.0102020 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
총 4개의 GC 로그입니다. 로그의 첫 번째 줄을 보면 "2018-08-02T14:39:11.560-0800"이 UTC 표준시 형식으로 정확합니다. 밀리초 수준으로, "-XX:+PrintGCDateStamps" 매개변수는 gc 로그 다음에 이 타임스탬프를 인쇄하도록 구성됩니다. "10.171"은 JVM 시작부터 gc 발생까지 경과된 시간(초)입니다. 로그 텍스트의 첫 번째 줄 시작 부분에 있는 "[GC"는 이 GC에서 Stop-The-World가 발생하지 않았음을 나타냅니다(사용자 스레드가 일시 중지됨). 로그 두 번째 줄 시작 부분에 있는 "[Full GC" 텍스트는 이 GC에서 Stop-The-World가 발생했음을 나타내므로 [GC 및 Full GC]는 신세대 및 구세대와 관련이 없으며 System을 호출하는 경우에는 관련이 있습니다. .gc()를 직접 실행하면 [Full GC(System)이 표시됩니다. 다음 "[PSYoungGen" 및 "[ParOldGen"은 GC가 발생하는 영역을 나타냅니다. 표시된 특정 이름은 가비지 수집기와도 관련이 있습니다. 예를 들어 여기서 "[PSYoungGen"은 병렬 Scavenge 수집기를 나타내고 "[ParOldGen"은 GC를 나타냅니다. Serial Old 콜렉터는 또한 Serial 콜렉터에 "[DefNew"를 표시하고 ParNew 콜렉터에는 "[ParNew" 등을 표시합니다. 다음 "30128K->4091K(30208K)"는 이번 gc 이후 이 영역의 메모리 사용 공간이 30128K에서 4091K로 줄어들어 전체 메모리 크기가 30208K임을 나타냅니다. 각 영역의 gc 설명 뒤에 "51092K->50790K(98816K), 0.0140970초" 이번 가비지 컬렉션 이후 전체 힙 메모리의 메모리 사용량이 51092K에서 50790K로 줄었고, 전체 힙 메모리의 전체 공간도 줄었습니다. 98816K였습니다. gc에는 0.0140970초가 걸렸습니다.
4 스레드 스냅샷: 이름에서 알 수 있듯이 스레드 스냅샷을 기반으로 특정 순간의 스레드 상태를 확인할 수 있습니다. 시스템에 요청 시간 초과, 무한 루프, 교착 상태 등이 있을 수 있습니다. 스레드 스냅샷을 기반으로 문제를 파악합니다. 가상 머신과 함께 제공되는 "jstack pid" 명령을 실행하면 현재 프로세스의 스레드에 대한 스냅샷 정보를 덤프할 수 있습니다. 더 자세한 사용 및 분석을 위한 예제는 이미 많이 있습니다. 자세한 내용은 다루지 않겠습니다. 참고용으로 블로그를 게시하세요. http://www.cnblogs.com/kongzhongqijing/articles/3630264.html
⑤힙 덤프 스냅샷: "-XX:+HeapDumpOnOutOfMemory"를 사용할 수 있습니다. "-XX:" 프로그램이 시작되면 HeapDumpPath=/data/jvm/dumpfile.hprof", 프로그램에서 메모리 오버플로가 발생하면 해당 시점의 메모리 스냅샷을 파일 형식으로 덤프합니다(직접 할 수도 있습니다). 프로그램이 실행 중일 때 언제든지 jmap 명령을 사용하여 메모리 스냅샷을 덤프합니다. 이후 해당 시점의 메모리 사용량을 분석합니다.
(2) JVM 튜닝 도구
①用 jps(JVM process Status)可以查看虚拟机启动的所有进程、执行主类的全名、JVM启动参数,比如当执行了JPSTest类中的main方法后(main方法持续执行),执行 jps -l可看到下面的JPSTest类的pid为31354,加上-v参数还可以看到JVM启动参数。
3265 32914 sun.tools.jps.Jps 31353 org.jetbrains.jps.cmdline.Launcher 31354 com.danny.test.code.jvm.JPSTest 380
②用jstat(JVM Statistics Monitoring Tool)监视虚拟机信息
jstat -gc pid 500 10 :每500毫秒打印一次Java堆状况(各个区的容量、使用容量、gc时间等信息),打印10次
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 11264.0 11264.0 11202.7 0.0 11776.0 1154.3 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 4037.0 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 6604.5 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 9487.2 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 0.0 0.0 11776.0 258.1 68608.0 58983.4 - - - - 15 0.082 8 0.059 0.141 11264.0 11264.0 0.0 0.0 11776.0 3076.8 68608.0 58983.4 - - - - 15 0.082 8 0.059 0.141 11264.0 11264.0 0.0 0.0 11776.0 0.0 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 0.0 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 258.1 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 3012.8 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149
jstat还可以以其他角度监视各区内存大小、监视类装载信息等,具体可以google jstat的详细用法。
③用jmap(Memory Map for Java)查看堆内存信息
执行jmap -histo pid可以打印出当前堆中所有每个类的实例数量和内存占用,如下,class name是每个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的所有示例占用内存大小,instances是这个类的实例数量:
num #instances #bytes class name ---------------------------------------------- 1: 2291 29274080 [B 2: 15252 1961040 <methodKlass> 3: 15252 1871400 <constMethodKlass> 4: 18038 721520 java.util.TreeMap$Entry 5: 6182 530088 [C 6: 11391 273384 java.lang.Long 7: 5576 267648 java.util.TreeMap 8: 50 155872 [I 9: 6124 146976 java.lang.String 10: 3330 133200 java.util.LinkedHashMap$Entry 11: 5544 133056 javax.management.openmbean.CompositeDataSupport
执行 jmap -dump 可以转储堆内存快照到指定文件,比如执行 jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof 3361 可以把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,然后可以对内存快照进行分析。
④利用jconsole、jvisualvm分析内存信息(各个区如Eden、Survivor、Old等内存变化情况),如果查看的是远程服务器的JVM,程序启动需要加上如下参数:
"-Dcom.sun.management.jmxremote=true" "-Djava.rmi.server.hostname=12.34.56.78" "-Dcom.sun.management.jmxremote.port=18181" "-Dcom.sun.management.jmxremote.authenticate=false" "-Dcom.sun.management.jmxremote.ssl=false"
下图是jconsole界面,概览选项可以观测堆内存使用量、线程数、类加载数和CPU占用率;内存选项可以查看堆中各个区域的内存使用量和左下角的详细描述(内存大小、GC情况等);线程选项可以查看当前JVM加载的线程,查看每个线程的堆栈信息,还可以检测死锁;VM概要描述了虚拟机的各种详细参数。(jconsole功能演示)
下图是jvisualvm的界面,功能比jconsole略丰富一些,不过大部分功能都需要安装插件。概述跟jconsole的VM概要差不多,描述的是jvm的详细参数和程序启动参数;监视展示的和jconsole的概览界面差不多(CPU、堆/方法区、类加载、线程);线程和jconsole的线程界面差不多;抽样器可以展示当前占用内存的类的排行榜及其实例的个数;Visual GC可以更丰富地展示当前各个区域的内存占用大小及历史信息(下图)。(jvisualvm功能演示)
⑤分析堆转储快照
前面说到配置了 “-XX:+HeapDumpOnOutOfMemory” 参数可以在程序发生内存溢出时dump出当前的内存快照,也可以用jmap命令随时dump出当时内存状态的快照信息,dump的内存快照一般是以.hprof为后缀的二进制格式文件。
可以直接用 jhat(JVM Heap Analysis Tool) 命令来分析内存快照,它的本质实际上内嵌了一个微型的服务器,可以通过浏览器来分析对应的内存快照,比如执行 jhat -port 9810 -J-Xmx4G /data/jvm/dumpfile_jmap.hprof 表示以9810端口启动 jhat 内嵌的服务器:
Reading from /Users/dannyhoo/data/jvm/dumpfile_jmap.hprof... Dump file created Fri Aug 03 15:48:27 CST 2018 Snapshot read, resolving... Resolving 276472 objects... Chasing references, expect 55 dots....................................................... Eliminating duplicate references....................................................... Snapshot resolved. Started HTTP server on port 9810 Server is ready.
在控制台可以看到服务器启动了,访问 http://127.0.0.1:9810/ 可以看到对快照中的每个类进行分析的结果(界面略low),下图是我随便选择了一个类的信息,有这个类的父类,加载这个类的类加载器和占用的空间大小,下面还有这个类的每个实例(References)及其内存地址和大小,点进去会显示这个实例的一些成员变量等信息:
jvisualvm也可以分析内存快照,在jvisualvm菜单的“文件”-“装入”,选择堆内存快照,快照中的信息就以图形界面展示出来了,如下,主要可以查看每个类占用的空间、实例的数量和实例的详情等:
还有很多分析内存快照的第三方工具,比如eclipse mat,它比jvisualvm功能更专业,出了查看每个类及对应实例占用的空间、数量,还可以查询对象之间的调用链,可以查看某个实例到GC Root之间的链,等等。可以在eclipse中安装mat插件,也可以下载独立的版本(http://www.eclipse.org/mat/downloads.php ),我在mac上安装后运行起来老卡死~下面是在windows上的截图(MAT功能演示):
(3) JVM 튜닝 경험
JVM 구성 측면에서는 일반적으로 기본 구성을 먼저 사용할 수 있습니다(일부 기본 초기 매개변수는 시스템 작동 상태에 따라 테스트 중에 일반 애플리케이션이 보다 안정적으로 실행되도록 할 수 있습니다). (세션 동시성), 세션 시간 등), gc 로그, 메모리 모니터링 및 사용된 가비지 수집기를 기반으로 합리적으로 조정합니다. 이전 세대 메모리가 너무 작을 경우 메모리가 너무 많으면 빈번한 Full GC가 발생할 수 있습니다. 규모가 크면 Full GC 시간이 특히 길어집니다.
그럼 신세대, 구세대 등 가장 적합한 JVM 구성은 무엇일까요? 튜닝은 물리적인 메모리가 확실할 때 New Generation 설정이 클수록 Old Generation이 작아지고 Full GC 빈도는 높아지지만 Full GC 시간은 짧아지는 과정입니다. 반대로 New Generation 설정은 크기가 작을수록 Old Generation의 크기가 커지고 Full GC의 빈도는 낮아지지만 각 Full GC에 소요되는 시간이 늘어납니다. 제안 사항은 다음과 같습니다.
-Xms 및 -Xmx 값을 동일하게 설정합니다. 힙 크기는 기본 여유 힙 메모리가 40% 미만인 경우 기본적으로 -Xms로 지정됩니다. JVM은 -Xmx에 지정된 크기로 힙을 확장합니다. 여유 힙 메모리가 70%보다 크면 JVM은 -Xms에 지정된 크기로 힙을 줄입니다. Full GC 이후에 메모리 요구 사항을 충족할 수 없으면 동적으로 조정됩니다. 이 단계는 상대적으로 리소스를 많이 소모합니다.
객체가 신세대에서 더 오랫동안 생존할 수 있도록 신세대를 최대한 크게 설정합니다. 각 Minor GC는 객체가 이전 세대에 들어갈 가능성을 방지하거나 지연하기 위해 가능한 한 많은 가비지 객체를 수집해야 합니다. Full GC의 빈도를 줄이기 위한 생성입니다.
구세대에서 CMS 컬렉터를 사용한다면 신세대는 너무 클 필요가 없습니다. 왜냐하면 CMS의 병렬 수집 속도도 매우 빠르고, 동시 마킹과 동시 클리어 단계에서 시간이 많이 걸리기 때문입니다. 수집 프로세스는 사용자 스레드와 동시에 실행될 수 있습니다.
메소드 영역의 크기를 설정할 때 1.6 이전에는 시스템 실행 시 동적으로 추가되는 상수, 정적 변수 등을 고려해야 하지만 1.7에서는 해당 크기만 수용할 수 있으면 됩니다. 시작 시 및 나중에 동적으로 로드되는 클래스 정보입니다.
코드 구현 측면에서 프로그램 대기 및 메모리 누수와 같은 성능 문제는 JVM 구성에 발생할 수 있는 문제 외에도 코드 구현과도 많은 관련이 있습니다. 배열: 지나치게 큰 개체 또는 새 세대에 배열을 수용할 공간이 충분하지 않은 경우, 수명이 짧은 대형 개체인 경우 미리 Full GC가 시작됩니다.
한 번에 데이터베이스에서 많은 양의 데이터를 가져오거나, Excel에서 많은 수의 레코드를 읽는 등 많은 양의 데이터를 동시에 로드하는 것을 피하고 일괄적으로 읽을 수 있습니다. 가능한 한 빨리 참조를 삭제하세요.
컬렉션에 개체에 대한 참조가 있는 경우 해당 개체를 사용한 후 가능한 한 빨리 컬렉션에 있는 참조를 삭제해야 합니다. 이러한 쓸모없는 개체는 노후화를 방지하기 위해 최대한 빨리 재활용해야 합니다.
적절한 시나리오(예: 캐싱 구현)에서 소프트 참조와 약한 참조를 사용할 수 있습니다. 예를 들어 소프트 참조를 사용하여 ObjectA에 인스턴스를 할당합니다. SoftReference objectA=new SoftReference(); 목록에 포함됩니다. 재활용 범위는 두 번 재활용됩니다. 이 재활용에 필요한 메모리가 충분하지 않으면 메모리 오버플로 예외가 발생합니다.
무한 루프가 발생하지 않도록 하세요. 무한 루프가 발생한 후 루프 본체에 많은 인스턴스가 반복적으로 생성되어 메모리 공간이 빠르게 채워질 수 있습니다.
외부 리소스(데이터베이스, 네트워크, 장비 리소스 등)를 오랫동안 기다리는 것을 피하고, 개체의 수명 주기를 단축하며, 결과를 제때 반환할 수 없는 경우 노후화를 피하도록 노력하세요. 비동기 처리를 적절하게 사용할 수 있습니다.
(4) JVM 문제 해결 기록 사례
잦은 Full GC 프로세스의 잊지 못할 문제 해결 http: //caogen81 .iteye.com/blog/1513345
온라인 FullGC의 빈번한 문제 해결 https://blog.csdn.net/wilsonpeng3/article/details/70064336/
[JVM] 온라인 애플리케이션 문제 해결 https:/ /www.cnblogs.com /Dhouse/p/7839810.html
JVM의 FullGC 문제 해결 과정 http://iamzhongyong.iteye.com/blog/1830265
JVM 메모리 오버플로로 인한 높은 CPU 문제 해결 사례 https://blog .csdn.net/nielinqi520/article/details/78455614
Java 메모리 누수 문제 해결 사례 https://blog.csdn.net/aasgis6u/article/details/54928744
(5) 공통 JVM 매개변수 참조:
Parameter | Description | Instance |
---|---|---|
-Xms | 초기 힙 크기, 기본 물리적 메모리의 1/64 | -Xms512M |
-Xm x | 최대 힙 크기, 기본값 물리적으로 메모리의 1/4 | -Xms2G |
-Xmn | 신세대 메모리 크기, 공식 권장 사항은 전체 힙의 3/8입니다 | -Xmn512M |
-Xss | 스레드 스택 크기 , jdk1.5 이후 기본값은 1M, 이전 기본값은 256k | -Xss512k |
-XX:NewRatio=n | 신세대와 구세대의 비율을 설정합니다. 예를 들어 is 3은 젊은 세대와 기성 세대의 비율이 1:3이라는 뜻이고, 젊은 세대는 전체 젊은 세대와 기성 세대의 1/4을 차지한다는 뜻입니다 | -XX:NewRatio=3 |
-XX:SurvivorRatio=n | 젊은 세대의 두 생존자 영역에 대한 에덴 영역의 비율입니다. 두 개의 생존자 영역이 있습니다. 예: 8은 Eden: Survivor=8:1:1을 의미하며 하나의 Survivor 영역은 전체 젊은 세대의 1/8을 차지합니다 | -XX:SurvivorRatio=8 |
-XX:PermSize=n | Initial 영구 생성 값, 기본값은 물리적 메모리의 1/64입니다 | -XX:PermSize=128M |
-XX:MaxPermSize=n | 최대 영구 생성 값, 기본값은 물리적 메모리의 1/4입니다. 물리적 메모리 | -XX:MaxPermSize=256M |
-verbose:class | 콘솔에 클래스 로딩 정보 인쇄 | |
-verbose:gc | 콘솔에 가비지 수집 로그 인쇄 | |
-XX:+PrintGC | GC 로그 인쇄, 간단한 콘텐츠 | |
-XX:+PrintGCDetails | GC 로그 인쇄, 세부 콘텐츠 | |
-XX:+PrintGCDateStamps | 시간 추가 밟아 넣다 GC 로그 | |
-Xloggc:파일 이름 | gc 로그 경로 지정 | -Xloggc:/data/jvm/gc.log |
-XX:+UseSerialGC | Young 세대 세트 직렬 수집기 Serial | |
-XX :+UseParallelGC | 젊은 세대는 병렬 수집기 Parallel Scavenge를 설정합니다. | |
-XX:ParallelGCThreads=n | Parallel Scavenge가 수집할 때 사용되는 CPU 수를 설정합니다. 병렬 수집 스레드 수입니다. | -XX:ParallelGCThreads=4 |
-XX:MaxGCPauseMillis=n | 병렬 청소 재활용의 최대 시간 설정(밀리초) | -XX:MaxGCPauseMillis=100 |
-XX:GCTimeRatio= ㄴ | 병렬 청소 가비지 수집 시간을 프로그램 실행 시간의 백분율로 설정합니다. 공식은 1/(1+n) | -XX:GCTimeRatio=19 |
-XX:+UseParallelOldGC | Old Age를 병렬 수집기로 설정 ParallelOld Collector | |
-XX:+UseConcMarkSweepGC | 구세대 동시 수집기 CMS를 설정하세요 | |
-XX:+CMSIncrementalMode | CMS 수집기를 단일 CPU 상황에 적합한 증분 모드로 설정하세요. |
이 기사는 PHP 중국어 웹사이트, java tutorial 칼럼에서 가져온 것입니다. 배우기를 환영합니다!
위 내용은 jvm에 성능 조정이 필요한 이유는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!