>  기사  >  Java  >  면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

Java后端技术全栈
Java后端技术全栈앞으로
2023-08-17 16:38:451024검색

OOM은 개발자들이 가장 두려워하는 문제 중 하나라고 할 수 있으며, 그 원인은 기본적으로 코드나 JVM 매개변수 구성에 의해 발생합니다.

이 기사에서는 Java 프로세스가 OOM을 트리거한 후 문제를 해결하는 방법에 대해 독자에게 설명합니다.

제작 환경에 대한 경외심이 많다고 하는데, 문제를 빠르게 해결하는 것도 경외감의 표시입니다

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

OOM

OOM은 "Out Of Memory"를 뜻하는 "Out Of Memory"의 약자입니다. 메모리가 소진되었음을 의미합니다. JVM에 객체에 할당할 공간이 부족하고 가비지 수집기에 재활용할 공간이 없으면 이 오류가 발생합니다

OOM이 발생하는 이유는 일반적으로 이러한 문제 때문입니다

  1. 너무 적습니다. 할당 : JVM 초기화 메모리가 적고, 업무상 메모리를 많이 사용하거나, JVM 영역별로 메모리 할당이 무리하다
  2. 코드 취약점: 특정 객체를 자주 신청하지만 해제하지 않는다. 더 이상 사용되지 않아 메모리 소진이 발생합니다

메모리 누수: 적용한 메모리가 해제되지 않아 가상 머신에서 해당 메모리를 다시 사용할 수 없게 됩니다. 이때 이 메모리가 누수됩니다. . 신청자는 더 이상 사용하지 않지만 가상머신을 다른 사람에게 할당할 수 없기 때문입니다.

메모리 오버플로: 적용된 메모리가 JVM이 제공할 수 있는 메모리 크기를 초과하는 경우를 오버플로라고 합니다.

메모리 누수가 계속되고 결국에는 오버플로되어야 합니다. 둘은 인과적으로 관련되어 있습니다.

Common OOM

더 일반적인 OOM 유형은 다음과 같습니다

java.lang.OutOfMemoryError: PermGen space

Java7 영구 생성(메서드 영역) 오버플로, 클래스 정보, 상수, 정적 변수, JIT(Just-In-Time) 컴파일러로 컴파일된 코드 등 가상 머신에서 로드한 데이터를 저장하는 데 사용됩니다. 클래스가 처음 로드될 때마다 메타데이터는 영구 생성에 저장됩니다

일반적으로 다수의 클래스 객체 또는 JSP 페이지에 나타나거나 CgLib 동적 프록시 기술을 사용하여 나타납니다

-XX: PermSize-XX:MaxPermSize 메소드 영역의 크기 수정-XX:PermSize-XX:MaxPermSize 修改方法区大小

Java8 将永久代变更为元空间,报错:java.lang.OutOfMemoryError: Metadata space,元空间内存不足默认进行动态扩展

java.lang.StackOverflowError

虚拟机栈溢出,一般是由于程序中存在 死循环或者深度递归调用 造成的。如果栈大小设置过小也会出现溢出,可以通过 -Xss 设置栈的大小

虚拟机抛出栈溢出错误,可以在日志中定位到错误的类、方法

java.lang.OutOfMemoryError: Java heap space

Java 堆内存溢出,溢出的原因一般由于 JVM 堆内存设置不合理或者内存泄漏导致

如果是内存泄漏,可以通过工具查看泄漏对象到 GC Roots 的引用链。掌握了泄漏对象的类型信息以及 GC Roots 引用链信息,就可以精准地定位出泄漏代码的位置

如果不存在内存泄漏,就是内存中的对象确实都还必须存活着,那就应该检查虚拟机的堆参数(-Xmx 与 -Xms),查看是否可以将虚拟机的内存调大些

小结:方法区和虚拟机栈的溢出场景不在本篇过多讨论,下面主要讲解常见的 Java 堆空间的 OOM 排查思路

查看 JVM 内存分布

假设我们 Java 应用 PID 为 15162,输入命令查看 JVM 内存分布 jmap -heap 15162

Java8은 영구 생성을 메타스페이스로 변경하고 오류가 보고됩니다: java.lang.OutOfMemoryError: Metadata space. 메모리가 부족하고 기본적으로 동적으로 확장됩니다

java.lang.StackOverflowError

🎜가상 머신 스택 오버플로, 일반적으로 infinite의 존재로 인해 발생 프로그램에서 루프 또는 심층 재귀 호출이 발생했습니다. 스택 크기를 너무 작게 설정하면 오버플로가 발생합니다. -Xss 스택 크기 설정 🎜🎜가상 머신에서 스택 오버플로 오류가 발생합니다. 로그🎜🎜java에서 잘못된 클래스와 메소드를 찾을 수 있습니다. lang.OutOfMemoryError: Java 힙 공간🎜🎜Java 힙 메모리 오버플로, 오버플로는 일반적으로 불합리한 JVM 힙 메모리 설정 또는 메모리 누수로 인해 발생합니다.🎜🎜메모리 누수인 경우 다음을 수행할 수 있습니다. 도구를 사용하여 GC Roots의 참조 체인을 확인하세요. 유출된 객체의 타입 정보와 GC Roots 참조 체인 정보를 숙지하면 유출된 코드의 위치를 ​​정확하게 찾을 수 있습니다🎜🎜메모리 누수가 없다면, 즉 메모리에 있는 객체는 아직 살아 있어야 합니다. 가상 머신의 메모리를 늘릴 수 있는지 확인하려면 가상 머신 힙 매개변수(-Xmx 및 -Xms)를 확인해야 합니다.🎜🎜요약: 메서드 영역과 가상 머신 스택의 오버플로 시나리오는 여기서 많이 논의되지 않습니다. 다음 기사에서는 일반적인 Java 힙 공간 OOM 문제 해결 아이디어를 주로 설명합니다.🎜

JVM 메모리 분포 보기

🎜Java 애플리케이션의 PID가 15162라고 가정합니다. JVM 메모리 분포를 보려면 명령을 입력하세요jmap -heap 15162🎜
[xxx@xxx ~]# jmap -heap 15162
Attaching to process ID 15162, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.161-b12

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40 # 最小堆使用比例
   MaxHeapFreeRatio         = 70 # 最大堆可用比例
   MaxHeapSize              = 482344960 (460.0MB) # 最大堆空间大小
   NewSize                  = 10485760 (10.0MB) # 新生代分配大小
   MaxNewSize               = 160759808 (153.3125MB) # 最大新生代可分配大小
   OldSize                  = 20971520 (20.0MB) # 老年代大小
   NewRatio                 = 2 # 新生代比例
   SurvivorRatio            = 8 # 新生代与 Survivor 比例
   MetaspaceSize            = 21807104 (20.796875MB) # 元空间大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB) # Compressed Class Space 空间大小限制
   MaxMetaspaceSize         = 17592186044415 MB # 最大元空间大小
   G1HeapRegionSize         = 0 (0.0MB) # G1 单个 Region 大小

Heap Usage:  # 堆使用情况
New Generation (Eden + 1 Survivor Space): # 新生代
   capacity = 9502720 (9.0625MB) # 新生代总容量
   used     = 4995320 (4.763908386230469MB) # 新生代已使用
   free     = 4507400 (4.298591613769531MB) # 新生代剩余容量
   52.56726495150862% used # 新生代使用占比
Eden Space:  
   capacity = 8454144 (8.0625MB) # Eden 区总容量
   used     = 4029752 (3.8430709838867188MB) # Eden 区已使用
   free     = 4424392 (4.219429016113281MB) # Eden 区剩余容量
   47.665996699370154% used  # Eden 区使用占比
From Space: # 其中一个 Survivor 区的内存分布
   capacity = 1048576 (1.0MB)
   used     = 965568 (0.92083740234375MB)
   free     = 83008 (0.07916259765625MB)
   92.083740234375% used
To Space: # 另一个 Survivor 区的内存分布
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
tenured generation: # 老年代
   capacity = 20971520 (20.0MB)
   used     = 10611384 (10.119804382324219MB)
   free     = 10360136 (9.880195617675781MB)
   50.599021911621094% used

10730 interned Strings occupying 906232 bytes.
🎜JVM 메모리 할당 및 런타임 사용량을 확인하여 메모리 할당이 합리적인지 판단할 수 있습니다🎜

또한 JVM이 실행되는 동안 리소스를 가장 많이 소비하는 개체를 볼 수 있습니다. jmap -histo:live 15162 | 더보기jmap -histo:live 15162 | more

JVM 内存对象列表按照对象所占内存大小排序

  • instances:实例数
  • bytes:单位 byte
  • class name:类名
면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

明显看到 CustomObjTest 对象实例以及占用内存过多

可惜的是,方案存在局限性,因为它只能排查对象占用内存过高问题

其中 "[" 代表数组,例如 "[C" 代表 Char 数组,"[B" 代表 Byte 数组。如果数组内存占用过多,我们不知道哪些对象持有它,所以就需要 Dump 内存进行离线分析

jmap -histo:live

JVM 메모리 객체 목록은 객체가 차지하는 메모리 크기에 따라 정렬됩니다
  • 인스턴스: 인스턴스 수
  • 바이트: 단위 바이트
  • 클래스 이름: 클래스 이름
면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

분명히 CustomObjTest 객체 인스턴스가 너무 많은 메모리를 차지합니다안타깝게도 솔루션에는 제한이 있습니다. 너무 많은 메모리를 차지하는 객체의 문제만 해결할 수 있기 때문입니다. 여기서 "["는 배열을 나타냅니다. 예를 들어 "[C"는 Char 배열을 나타내고 "[B"는 Byte 배열을 나타냅니다. 배열 메모리가 너무 많이 차지하면 어떤 개체가 이를 보유하고 있는지 알 수 없으므로 오프라인 분석을 위해 메모리를 덤프해야 합니다

jmap -histo:live 이 명령을 실행하면 JVM이 먼저 GC를 트리거한 다음 통계 정보를 수집합니다

Dump 파일 분석 Dump 파일은 주로 시스템 정보,

가상 머신 속성

, 전체 스레드 덤프,

모든 클래스와 객체의 상태

및 기타 정보

🎜를 포함하는 Java 프로세스의 메모리 이미지입니다. 또는 GC 예외가 발생하면 JVM이 발생한 것으로 의심됩니다🎜 메모리 누수🎜, 그러면 분석을 위해 덤프 파일을 내보낼 수 있습니다🎜🎜JVM 시작 매개변수 구성에 다음 매개변수를 추가하세요🎜
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=./(参数为 Dump 文件生成路径)

当 JVM 发生 OOM 异常自动导出 Dump 文件,文件名称默认格式:java_pid{pid}.hprof

上面配置是在应用抛出 OOM 后自动导出 Dump,或者可以在 JVM 运行时导出 Dump 文件

jmap -dump:file=[文件路径] [pid]

# 示例
jmap -dump:file=./jvmdump.hprof 15162

在本地写一个测试代码,验证下 OOM 以及分析 Dump 文件

设置 VM 参数:-Xms3m -Xmx3m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

public static void main(String[] args) {
    List<Object> oomList = Lists.newArrayList();
   // 无限循环创建对象
    while (true) {
        oomList.add(new Object());
    }
}

通过报错信息得知,java heap space 表示 OOM 发生在堆区,并生成了 hprof 二进制文件在当前文件夹下

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

JvisualVM 分析

Dump 分析工具有很多,相对而言 JvisualVMJProfilerEclipse Mat,使用人群更多一些。下面以 JvisualVM 举例分析 Dump 文件

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

列举两个常用的功能,第一个是能看到触发 OOM 的线程堆栈,清晰得知程序溢出的原因

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

第二个就是可以查看 JVM 内存里保留大小最大的对象,可以自由选择排查个数

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

点击对象还可以跳转具体的对象引用详情页面

면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?

文中 Dump 文件较为简单,而正式环境出错的原因五花八门,所以不对该 Dump 文件做深度解析

注意:JvisualVM 如果分析大 Dump 文件,可能会因为内存不足打不开,需要调整默认的内存

요약 검토

온라인에서 JVM 메모리 오버플로가 발생하는 경우 다음 단계에 따라 문제를 해결할 수 있습니다.

  1. jmap -heap 메모리 할당이 너무 작은지 확인하세요jmap -heap 查看是否内存分配过小
  2. jmap -histo 查看是否有明显的对象分配过多且没有释放情况
  3. jmap -dump
jmap -histo 명백한 객체 할당이 너무 많고 해제되지 않음

jmap -dump JVM의 현재 메모리 스냅샷을 내보내고 JDK 또는 MAT와 같은 도구를 사용하여 스냅샷을 분석합니다. 🎜🎜🎜🎜위에서 문제를 찾을 수 없는 경우 다음이 필요합니다. 예를 들어, 네트워크 연결이나 스레드로 인해 시스템 리소스가 고갈될 수 있습니다. 🎜🎜🎜

위 내용은 면접관: 온라인에서 OOM을 접하게 된다면 어떻게 해결하나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Java后端技术全栈에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제