서버 프로세스 종료 문제 해결 (metaspace 넘침) 실전

10636 단어 jvm 조정백엔드

현상


기획 반응 서버가 들어가지 못하고 원격으로 프로세스가 사라졌습니다(crash), 때때로 로그인할 수 있지만 실행할 수 없습니다. (프로세스가 아직 있습니다), 정상적인 shutdown을 받을 수 없습니다. 프로세스 루트 디렉터리에 java_pid16298.hprof 파일이 나왔습니다. 보기만 해도 메모리가 넘쳤습니다. 이상하게 메모리가 넘치지 않을 것 같습니다. 인원수가 많지 않기 때문에 영구 넘침(Java8#Metaspace)으로 의심됩니다. 다음은 과연 검증되었습니다.시작 매개 변수가 추가되었기 때문에-XX:+HeapDumpOnOutOfMemoryError, 즉 JVM 치명적인 오류 로그가 발생했습니다.

로그 조회(vim/grep/less/more)

  • vim std.logesc/OutOfMemoryError ?OutOfMemoryErrorn/N 다음
  • less std.log | grep OutOfMemoryErrorCaused by: java.lang.OutOfMemoryError: Metaspace
  • grep OutOfMemory std.log - A 50 - B 50 | less/OutOfMemoryError n 다음 q 종료
  • less std.log에 콜론/OutOfMemoryError 검색 q가 나타나도 됩니까?OutOfMemoryError
  • more std.log/OutOfMemoryError 검색 q 종료는/
  • 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 메모리 넘침, 내 시작 매개 변수 설정 크기는 48Mhs_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 디버깅을 통해)
  • deserializer
  • committed 49152K, 예를 들어 reserved 1093632K를 호출하면 clazz에 대응하는 예를 생성ASMDeserializerFactory#createJavaBeanDeserializer류로 반서열화를 하는데 현재 사용되는 곳은 프로필, 데이터 테이블, 유저 관련 데이터 등을 포함한다.
  • serializer

  • 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 호출로 바꿉니다.
    인터넷에는 이 메모리 넘침 문제로 인해 현재 업무 논리에서 빈번하게 반사되는 부분을 검색할 수 있다는 내용이 많다.
  • handler 논리적 방법의 반사 집행
  • protobuf의 반서열화
  • 기타 삼방 라이브러리의 반사 등
  • 도구 사용


    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.GeneratedConstructorAccessorcom.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으로 조정하여 실제 달리기 테스트를 준비해야 한다
  • Lambda 표현식은 내부 클래스를 생성합니다
  • 잦은 JVM 반사 호출에도 적절한 클래스 생성
  • 총결산


    프로세스crash의 대가가 매우 크기 때문에 비교적 큰metaspace를 설정할 수 있지만 누설되면 프로세스가 직접crash에 영향을 미치기 때문에 이 파라미터를 설정하지 않고 jvm가 스스로 조절하는 것을 권장합니다.만약 정말 유출이 발생한다면 메모리는 계속 길어질 것이다. 이때 우리의 운영 모니터링 시스템은 감청할 수 있다. 즉시 경보를 하고 정상적인 shutdown(shutdown 전에 jmap hprof)을 걸어서 문제를 조사할 수 있다.ps:Out Of Memory crash 때 shutdownhook를 실행합니다. 하지만 프로세스가 갑자기 crash되면 유저 체험에 영향을 주고 유실을 초래할 수 있습니다.

    좋은 웹페이지 즐겨찾기