서버 프로세스 종료 문제 해결 (metaspace 넘침) 실전
현상
기획 반응 서버가 들어가지 못하고 원격으로 프로세스가 사라졌습니다(crash), 때때로 로그인할 수 있지만 실행할 수 없습니다. (프로세스가 아직 있습니다), 정상적인 shutdown을 받을 수 없습니다. 프로세스 루트 디렉터리에
java_pid16298.hprof
파일이 나왔습니다. 보기만 해도 메모리가 넘쳤습니다. 이상하게 메모리가 넘치지 않을 것 같습니다. 인원수가 많지 않기 때문에 영구 넘침(Java8#Metaspace)으로 의심됩니다. 다음은 과연 검증되었습니다.시작 매개 변수가 추가되었기 때문에-XX:+HeapDumpOnOutOfMemoryError
, 즉 JVM 치명적인 오류 로그가 발생했습니다.로그 조회(vim/grep/less/more)
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_40]
at java.lang.ClassLoader.defineClass(ClassLoader.java:760) ~[na:1.8.0_40]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_40]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_40]
at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_40]
at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_40]
at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_40]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_40]
at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_40]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_40]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_40]
로그 출력에서 볼 때: Metaspace 메모리 넘침, 내 시작 매개 변수 설정 크기는 48M
hs_err_pid.log
치명적인 로그의 출력을 보면 jvm가 Metaspace::allocate에서 치명적인 오류-XX:MaxMetaspaceSize=48m
,Metaspace used 47519K
,capacity 48950K
...유사한 로그를 볼 수 있습니다.why?
Metaspace 개념 이해: JVM 소스 분석 Metaspace 복호화java7과java8 중 일부는 원래permgen에 있던 데이터가 무더기로 옮겨졌고 JDK7부터 영구 세대의 제거 작업이 시작되었다. 영구 세대에 저장된 데이터의 일부는 Java Heap 또는 Native Heap로 옮겨졌다.그러나 영구대는 여전히 JDK7에 존재하고 완전히 제거되지 않았다. 기호 인용(Symbols)이native heap로 옮겨졌다.자면량(internedstrings)이javaheap로 이동;클래스의 정적 변수(classstatics)가javaheap로 옮겨졌습니다.
In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace.
어떤 것이 공간을 차지했는지 (개인 분석은 주로 생성된 종류)
fastjson#asm(debug 디버깅을 통해)
committed 49152K
, 예를 들어 reserved 1093632K
를 호출하면 clazz에 대응하는 예를 생성ASMDeserializerFactory#createJavaBeanDeserializer
류로 반서열화를 하는데 현재 사용되는 곳은 프로필, 데이터 테이블, 유저 관련 데이터 등을 포함한다.ASMSerializer Factory #createJavaBeanSerializer, JSON과 같은 호출.toJSONString(Object object)은 ASMSerializer_와 같이 object#clazz와 대응하는1_xx류, write/서열화에 사용됩니다.
둘을 합치면 200개 정도 된다.
lambda 표현식 내부 클래스, lambda 표현식을 사용하는 모든 곳에서 xx$Lambda$1과 같은 클래스가 약 150개 정도 생성됩니다. 다른 프로토타입과 같은 클래스는 약 200여 개, 다른 클래스는 $의 내부 클래스를 보았지만 생성된 클래스는 발견되지 않았습니다. 숫자를 포함하는 클래스를 검색했습니다. 일반적인 동적 생성과 유사한 클래스는 숫자 등이 있기 때문에 대량
JSON#parseObject(String text, Class clazz)
을 발견했습니다. 350여 개,동시에 같은 수의 선이 발견되었다.reflect.DelegatingClassLoader(한 클래스만 있고 대응하는 숫자만 있는 실례)..., 동일FastjsonASMDeserializer_53_xx
...분석 -이것은 반사 최적화입니다. sun.reflect.GeneratedMethodAccessor344
JVM은 처음에 JNI 방식을 기본적으로 사용했습니다. 같은 클래스의 호출 횟수가 일정한 값에 이르면 Java bytecode 호출로 바꿉니다.인터넷에는 이 메모리 넘침 문제로 인해 현재 업무 논리에서 빈번하게 반사되는 부분을 검색할 수 있다는 내용이 많다.
도구 사용
jvisualvm#hprof#플러그인 설치 가능
: Mon Sep 25 14:30:30 CST 2017
: D:xxlandontask2017.9server_errjava_pid16298.hprof
: 56.1 MB
: 47,508,830
: 7,743
: 568,577
: 380
: 2,703
: 0
OutOfMemoryError
OutOfMemoryError : queue-executor-handler-8
두 개의 hprof를 보면 7700여 개의 클래스를 불러올 때 메모리 넘침 오류가 발생했습니다. OQL 컨트롤러 #오른쪽 아래 #저장된 조회 #PermGen 분석 #클래스 마운트 유형을 발견했습니다. 재미있는 것을 발견했습니다. 대량의 xx$Lambda$143 같은 클래스를 발견했습니다. Lambda 표현식은 내부 클래스를 생성하는 것입니다. 출력을 보면 Lambda 표현식이 생성하는 내부 클래스 번호는 1부터 시작하고 +,현재 보이는 것은 151개의 Lambda 내부 클래스입니다. 클래스 정보 아래에서 일치하는 것을 직접 검색할 수 있습니다.
mat 사용
open heap dumpSize: 22.6 MB Classes: 7.5k Objects: 578k Class Loader: 357
JavaBasics#class loader explorer
Class Name | Defined Classes | No. of Instances
------------------------------------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader @ 0x800230b0 | 4,312 | 93,289
| 2,617 | 484,122
com.alibaba.fastjson.util.ASMClassLoader @ 0x805fd848| 129 | 129
com.alibaba.fastjson.util.ASMClassLoader @ 0x805e2858| 73 | 73
------------------------------------------------------------------------------------------
Class Name | Shallow Heap | Retained Heap
----------------------------------------------------------------------------------------
class sun.reflect.GeneratedMethodAccessor344 @ 0x80593e18| 0 | 568
class sun.reflect.GeneratedMethodAccessor343 @ 0x80593ee0| 0 | 568
class sun.reflect.GeneratedMethodAccessor342 @ 0x80593fa8| 0 | 568
class sun.reflect.GeneratedMethodAccessor341 @ 0x80594070| 0 | 568
class sun.reflect.GeneratedMethodAccessor340 @ 0x80594138| 0 | 568
class sun.reflect.GeneratedMethodAccessor339 @ 0x80594200| 0 | 568
class sun.reflect.GeneratedMethodAccessor338 @ 0x805942c8| 0 | 568
class sun.reflect.GeneratedMethodAccessor337 @ 0x80594390| 0 | 568
class sun.reflect.GeneratedMethodAccessor336 @ 0x80594458| 0 | 568
...
----------------------------------------------------------------------------------------
Class Name | Defined Classes | No. of Instances
----------------------------------------------------------------------------------------------------
sun.misc.Launcher$ExtClassLoader @ 0x800230b0 | 4,312 | 93,289
| 2,617 | 484,122
com.alibaba.fastjson.util.ASMClassLoader @ 0x805fd848 | 129 | 129
com.alibaba.fastjson.util.ASMClassLoader @ 0x805e2858 | 73 | 73
javax.management.remote.rmi.NoCallStackClassLoader @ 0x806fc4f8| 1 | 0
javax.management.remote.rmi.NoCallStackClassLoader @ 0x806fc5d0| 1 | 0
sun.reflect.DelegatingClassLoader @ 0x80593db8 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80593e80 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80593f48 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594010 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x805940d8 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x805941a0 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594268 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594330 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x805943f8 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x805944c0 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594588 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594650 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x805947c8 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594890 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594958 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594a20 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594ae8 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594bb0 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594c78 | 1 | 1
sun.reflect.DelegatingClassLoader @ 0x80594d40 | 1 | 1
----------------------------------------------------------------------------------------------------
주요 기능
sun.reflect.GeneratedConstructorAccessor
com.alibaba.fastjson.util.ASMClassLoader(Deserializer_)#129com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_53_xxConfig, 왜 모든 Config 대상이 반서열화된 내부 클래스를 생성했는지 확인해야 합니까?그리고 다른 xx_RedisConfig 등(fastjson#asm 원리를 이해하면 됨)com.alibaba.fastjson.serializer.ASMSerializer_70_xxConfig
여기서 코드를 봤는데 xxMonsterConfig가 하나 있었어요. 이것은 redis로 서열화될 때 Serializer Feature를 추가하지 않았어요.Ignore Non Field Getter, 서열화된 몬고의 플레이어는 이 기능을 통일적으로 추가했습니다.
여기가 왜 ExtClassLoader가 업무의 큰 분류를 불러왔는지 설명해 드리겠습니다. 왜냐하면 이쪽에서 시작하는 것은 -Djava이기 때문입니다.ext.dirs=lib, 즉 ExtClassLoader가 불러온 것이지 AppClassLoader가 아니라 둘 다 OQL, OQL Syntax, SELECT DISTINCT OBJECTS classof(s) FROM'com.xx.*'s 조회 대상이 속한 클래스는com에 있습니다.xx 가방 아래에는 약 600여 개가 있다.
총결산과 해결 방법
위에서 분석한 바에 의하면 확실히 메타스페이스가 분배한 공간이 너무 적고 48M은 128M으로 조정하여 실제 달리기 테스트를 준비해야 한다
총결산
프로세스crash의 대가가 매우 크기 때문에 비교적 큰metaspace를 설정할 수 있지만 누설되면 프로세스가 직접crash에 영향을 미치기 때문에 이 파라미터를 설정하지 않고 jvm가 스스로 조절하는 것을 권장합니다.만약 정말 유출이 발생한다면 메모리는 계속 길어질 것이다. 이때 우리의 운영 모니터링 시스템은 감청할 수 있다. 즉시 경보를 하고 정상적인 shutdown(shutdown 전에 jmap hprof)을 걸어서 문제를 조사할 수 있다.ps:Out Of Memory crash 때 shutdownhook를 실행합니다. 하지만 프로세스가 갑자기 crash되면 유저 체험에 영향을 주고 유실을 초래할 수 있습니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
2018년 웹 개발자 로드맵 검토아직 늙지 않았다고 생각하고 싶지만 젊지 않은, 이번 주말에 34세의 Java 중심의 SE입니다. COBOL도 한 일이 있습니다. 아직도 지금의 웹 개발에 필요한 스킬을 익혀 가고 싶다. 어떤 스킬을 익히면 좋을까?...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.