A는 대용량 메모리 풋 프린트의 예를 찾을 수있는 jmap
샘플 코드를 제공 :
수입 인 java.util.ArrayList; 수입 은 java.util.List; 수입 java.util.concurrent.CountDownLatch; / ** * 2017년 5월 9일에菩提树下的杨过작성. * / 공용 클래스 OOMTest { 공공 정적 무효 메인 (문자열 [] 인수)이 발생 예외 : InterruptedException { CountDownLatch를 래치 = 새로운 CountDownLatch를 (1 ); INT 최대 = 10000 ; 목록은 <사람> 목록 = 새로운 ArrayList를을 <> (최대); 대 ( INT J = 0; J <최대, J ++ ) { 인 (P) = 새로운 인 (); p.setAge ( 100 ); p.setName ( "菩提树下的杨过" ); 에는 list.add (p); } 에서 System.out.println ( "준비!" ); latch.await (); } 공공 정적 클래스 사람 { 개인 문자열 이름; 개인 INT의 시대; 공공 문자열 getName () { 반환 이름; } 공공 무효 에서는 setName (문자열 이름) { 이.name과는 = 이름; } 공공 INT getAge () { 반환 시대; } 공공 무효 setAge ( INT의 연령) { 이 .age = 나이; } } }
예 목록이 프로그램을 넣고 실행 Person 객체를 넣어 1W
javac의 OOMTest.java
자바 OOMTest
그리고 창을 열고 는 -l을 JPS 프로그램의 PID를 찾을 수
그리고 할 수 있는 jmap -histo을 : 라이브 7320 (참고 : 내용의 출력이 너무 많이, 단지 10를보고 싶다면, 당신은 추가 할 수 있습니다 | 머리 -10)
출력 메모리 사용하여 것, 오브젝트, 내림차순 (바이트) 메모리의 양을 차지의 실제 수는 될 최종 출력의 요약을 출력 할
위의 예에서,이 클래스 10000 OOMTest $ 이용시의 경우의 수는 240,000 바이트의 총 차지 (주 : 각 인스턴스, 즉 24 바이트), 점유 프로그램 메모리의 총 수는 약 725,464 바이트이다 0.69 M.
아마 의미 일부 [C, [B 같은 클래스 이름도있다 :
[C는 숯불 []는
[S 짧은 []은
[I는 INT는이 []은
[B는 바이트 []은
[I는 INT이다 [] []
[String 오브젝트와 C는 종종 문자열을, 데이터를 유지하기 위해 그 내부 마지막 문자 [] 배열 관련
constMethodKlass / methodKlass / constantPoolKlass / constantPoolCacheKlass / instanceKlassKlass / methodDataKlass
관련 클래스 로더, 주민과 페름 지역.
둘째,? 스레드 및 핸들 자바 애플리케이션 개방의 수를 찾으려면
LL 은 / proc / PID} {/ FD | WC는 -l 볼 열린 핸들 번호
LL / proc 디렉토리 / {PID는} / 태스크 | WC는 -l은 스레드의 수를 볼 수 있습니다
세, 힙 메모리의 구성을 볼 수있는 jmap
jmap는 -heap PID 다음과 같은 출력과 같은 것을 볼 수 :
스레드 국부적하여 개체 . 할당 GC와 병렬 . 4 실 (S) // 본 실시 GC (GC 평행 한)이 사용될 : 힙 구성 // 힙 메모리 구성 MinHeapFreeRatio = 0 // 대응 JVM 기동 파라미터 -XX : MinHeapFreeRatio 배치 JVM 스택 최소 유휴 비율 (java8 디폴트 0) MaxHeapFreeRatio = 100 // 대응 JVM 기동 파라미터 -XX : MaxHeapFreeRatio 최대 유휴 JVM 스택 비율 설정 maxHeapSize = 8,388,608 ( . 8 .0MB)를 // 대응 JVM 기동 파라미터 -XX : maxHeapSize = JVM 스택 구비 최대 크기 (또는 -Xmx 파라미터) newSize와 = 5,242,880 ( . 5 .0MB) //JVM -XX 대응 시작 파라미터 : newSize와 배치 = JVM 스택 '차세대'기본 MaxNewSize = 5,242,880 ( . 5 .0MB) // 대응 JVM 기동 파라미터 -XX : MaxNewSize = 배치 JVM 스택 '차세대'최대 크기 OldSize = 3,145,728 ( 3. .0MB) // 대응 JVM 기동 파라미터 -XX : OldSize = 배치 JVM 스택 '구세대'크기 NewRatio = 2 // 대응 JVM 기동 파라미터 -XX : NewRatio = '차세대'와 '이전 소년 세대 '크기 비율 SurvivorRatio = 8. // 대응 JVM 기동 파라미터 -XX : SurvivorRatio = 에덴 젊은 세대 영역 크기 면적비 배치 서바이버 MetaspaceSize = 21,807,104 ( 20 .796875MB) CompressedClassSpaceSize =1073741824 ( 1024 .0MB) MaxMetaspaceSize = 17,592,186,044,415 MB G1HeapRegionSize = 0 ( 0 .0MB) 힙 사용법 : // 힙 메모리 사용 의 PS 젊은 세대 에덴 공간 : // 에덴의 유통 영역 용량 = 2,621,440 ( 2 .5MB) // 에덴 지역 총 용량이 사용되는 = 2,328,088 ( 2 .2202377319335938MB) // 에덴 영역이 사용 무료 = 293 (352) ( 0 .27976226806640625MB)를 //용량에게 남아있는 에덴 지역 88.80950927734375 %는 사용 우주에서을 : // 항에있어서, 생존자 메모리 유통 영역 용량 = 1,572,864 ( . 1 .5MB) 사용 = 360 (448) ( 0 .34375MB) 무료 = 1,212,416 ( . 1 .15625MB) 22.916666666666668 % 사용 받는 공간 : /을 / 메모리 레이아웃 영역 다른 서바이버 용량 = 1048576 ( . 1 .0MB) 사용 = 0 ( 0 .0MB) 무료 =1,048,576 ( . 1 .0MB) 0.0 % 사용 의 PS 구세대 // 현재 메모리 영역 분포 오래 용량 = 3,145,728 ( . 3 .0MB) 사용 = 1,458,968 ( . 1 .3913803100585938MB) 무료 = 1,686,760 ( . 1 .6086196899414062MB) 46.37934366861979 % 사용한 3759 구금 문자열 점령 298 824 바이트를.
참고 : 라인 5-16 힙 메모리는 기본 구성이며, 이러한 매개 변수 수있는 자바 -XX 수 : 같은 이름의 매개 변수 값, 매개 변수 크기 조정 =를 :
자바 -XX : MinHeapFreeRatio -XX = 20 : 80 = MaxHeapFreeRatio -Xmx100m -XX : MetaspaceSize = 50M -XX : NewRatio = 3 将 影响 MinHeapFreeRatio, MaxHeapFreeRatio, MaxHeapSize, MetaspaceSize, NewRatio 的 值
참고 NewRatio는,이 값이 오래된 년 (올드 세대)로 참조하는 : 새로운 세대 (젊은 세대) 3 위의 비율은 다음 그림을 참조하여, 25m에 OldSize의 75m 정도로 배열되고, newSize와있다 :
참고 :이 jdk7 개략도이다, jdk8 영구 세대가 제거 된, 새로운 기능 Metaspace 영역에 진입하지만, 새로운 세대는 이전 세대의 이해에 영향을주지 않습니다 차이.
새로운 세대 (젊은 세대) 에덴, S0, S1 세 블록으로 세분화 할 수있다.
Java7 실질적으로도되는 메모리 변경 java8.
SurvirorRatio는 좀 더 열심히 계산이 A는 오라클의 공식 웹 사이트에 따르면 설명 : https://docs.oracle.com/cd/E19159-01/819-3681/abeil/index.html , 기본 값, 즉 :? 각각의 생존자 8 : 에덴 영역의 크기는 1 : 8, 환언 S0 = S1 = 1 / (1 + 1 + 8) = 1/10
참고 : 공식 웹 사이트는 이렇게 설명하지만, 내 진짜 문제는,이 수에 엄격하게 비례하지 않는다 보이지만, 아마이 할당 비율이라고 할 수있다. (결론은 : 큰 SurvirorRatio 설정, 큰 에덴 영역)
넷째, 가장 높은 점유율 스레드 CPU를 찾을 수
코드의 데모에 먼저 :
import java.util.concurrent.CountDownLatch; /** * Created by 菩提树下的杨过 on 05/09/2017. */ public class OOMTest { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); int max = 100; for (int i = 0; i < max; i++) { Thread t = new Thread() { public void run() { try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }; t.setName("thread-" + i); t.start(); } Thread t = new Thread() { public void run() { int i = 0; while (true) { i = (i++) / 10; } } }; t.setName("BUSY THREAD"); t.start(); System.out.println("ready"); latch.await(); } }
这里面有100个线程是空转的,另外还有一个线程BUSY THREAD在狂跑CPU。
javac OOMTest.java
java OOMTest
把程序跑起来,jps -l 找出pid,然后 top -Hp pid
可以看到pid 16813这个对应的线程,把CPU快跑满了,达到了98.5%
接下来,将16813转换成16进制 ,即41ad (tips: printf "%x" 16813 ) ,然后
jstack pid | grep '41ad'
我们就把最忙的这个线程BUSY THREAD给找出来了(注:这个技巧再次说明了,给线程取个好名字相当重要!)
tips:如果使用spring-boot的话,直接在浏览器里查看/dump端点,也可以达到类似jstack的效果。
五、jvisualvm 查看运行情况
JDK_HOME/bin下有一个自带的jvisualvm工具,可以图形化的查看GC情况(注:要安装插件)
java.net这个网站已经被oracle关了,所以安装插件这里,有点小麻烦,先到https://visualvm.github.io/pluginscenters.html 这里找到jvisualvm对应的jdk版本号,以jdk8为例,地址就是 https://visualvm.github.io/uc/8u131/updates.xml.gz
然后,把这个地址在Plugins里的Settings里改一下,然后Available Plugin这里,就能看到可用插件了,选择GC插件并安装。
可以来一段代码,然后用jvisualvm来看下GC情况
import java.util.ArrayList; import java.util.List; /** * Created by 菩提树下的杨过 on 05/09/2017. */ public class OOMTest { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<>(); while (true) { Thread.sleep(10); list.add("菩提树下的杨过" + System.currentTimeMillis()); } } }
可以直观的看到Old区,Eden区,S0,S1以及Metaspace区的内存变化情况,以上图为例:Old Gen区占用内存一直在增加,表示可能有内存一直未被释放,值得关注。
此外,还可以看到占用内存最多的类(即:本文最开始提到的)
还可以更进一步点击看详情,比如下面的图,就能发现Metaspace已经OOM了
也可以查看哪些线程最忙
六、使用jstat 查看GC
虽然jvisualvm很好用,但是通常服务器是用终端连上的,无法运行图形化界面,而且也并非所有应用都开启了jmx,所以掌握jstat以命令行方式查看GC情况也是蛮重要的
用法:jstat -gc pid 采样间隔毫秒数,比如: jstat -gc 8544 5000,将每隔5s采样一次pid为8544的gc情况
以上图为例:红剪头的地方,S0区的已用量降到0,而S1区的已用量上涨,即说明发生了Young GC,对象从S0区被迁移到了S1区。
title栏的含义如下:
S0C - 新生代中第1块survivor 的容量(Survivor 0 Capacity),KB单位
S1C - 新生代中第2块survivor 的容量(Survivor 1 Capacity),KB单位
S0U - 新生代中第1块survivor 已使用空间数(Survivor 0 Used),KB单位
S1U - 新生代中第2块survivor 已使用空间数(Survivor 0 Used),KB单位
EC - Eden区的容量(KB)
EU - Eden区已使用(KB数)
OC - Old区的容量(KB)
OU - Old区已使用(KB数)
MC - Metaspace容量(KB)
MU - Metaspace已使用KB
CCSC - 压缩类的内存容量(KB)
CCSU - 压缩类的已用容量(KB)
YGC - (从应用启动算起,到采样时的) Young GC次数
YGCT - (从应用启动算起,到采样时的) Young GC所用时间(秒)
FGC - (从应用启动算起,到采样时的) Full GC次数
FGCT - (从应用启动算起,到采样时的) Full GC所用时间(秒)
GCT - (从应用启动算起,到采样时的) Yong GC + Full GC的总时间
值得一提的是G1垃圾回收器,在大堆(>4G)时,用G1可能效果会更好,G1的开启方法:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
켜져 때, 사용 jmap는 -heap PID
기본 동시 GC는 G1되고에서 우리는 볼 수 있습니다.
그러면 jstat -gc PID 5000
S0은 G1의 특징은 본 명세서의 마지막 참조하여 설명 된대로 새로운 세대, 분할 이전의 시대를 해제하지만, 영역의 새로운 개념과 함께, 전체 힙 메모리를 하나 개의 영역으로 분할되는 것 인, 모두 0을 참조 기사.
세븐, 전체 JVM의 수출 덤프
jmap의 -dump : B = 포맷, 파일 이름 = [PID]
내보낼 때이 최후의 수단이어야 확대, 전체 JVM은 다른 모든 수단이 작동하게되면 일반적으로 만 운영 및 유지 보수, 대량 내 보낸 파일에 참여할 것을 발견, 분석을 설정하고 응용 프로그램을 일시 중지됩니다 . 이 파일이 지역 얻을 후, 당신의 플러그인 일식 사용할 수있는 매트 : 다운로드 주소 분석 http://www.eclipse.org/mat/downloads.php을