>  기사  >  Java  >  Java에서 메모리 누수 및 메모리 오버플로란 무엇입니까?

Java에서 메모리 누수 및 메모리 오버플로란 무엇입니까?

青灯夜游
青灯夜游원래의
2021-09-22 17:48:3615316검색

메모리 누수는 프로그램이 메모리를 적용한 후 할당된 메모리 공간을 해제할 수 없음을 의미합니다. 메모리 오버플로는 프로그램이 메모리를 신청할 때 신청자가 사용할 메모리가 충분하지 않거나 int 데이터를 저장할 저장 공간을 제공하지만 긴 데이터가 저장되어 결과적으로 메모리가 충분하지 않은 것을 의미합니다. OOM 오류가 보고됩니다. 메모리 누수가 누적되면 결국 메모리 오버플로가 발생합니다.

Java에서 메모리 누수 및 메모리 오버플로란 무엇입니까?

이 튜토리얼의 운영 환경: windows7 시스템, java8 버전, DELL G3 컴퓨터.

1. 메모리 누수:

프로그램이 메모리를 적용한 후 적용된 메모리 공간을 해제할 수 없다는 의미입니다. 메모리 누수는 큰 영향을 미치지 않는 것 같지만 누적된 메모리 누수의 결과는 메모리입니다. 과다.

2. 메모리 오버플로 :

프로그램이 메모리를 신청할 때 신청자가 사용할 수 있는 메모리가 부족하다는 뜻, 즉 int형 데이터를 저장할 수 있는 저장 공간을 준다는 의미입니다. , 그러나 긴 유형의 데이터를 저장하면 결과적으로 메모리가 부족해 소위 메모리 오버플로라고 하는 OOM 오류가 보고됩니다.

3. 둘 사이의 관계:

  • 메모리 누수가 누적되면 결국 메모리 오버플로가 발생합니다.

  • 메모리 오버플로는 원하는 메모리 공간이 시스템에서 실제로 할당한 공간을 초과한다는 의미입니다. 이때 시스템이 사용자의 요구 사항을 충족할 수 없는 경우 메모리 오버플로 오류가 보고됩니다.

  • 메모리 누수란 시스템에 사용할 메모리 할당(신규)을 신청했지만 사용 후 반환(삭제)하지 않음을 의미합니다. 결과적으로 신청한 메모리에 더 이상 액세스할 수 없습니다(아마도 주소가 손실된 경우) 시스템은 해당 주소를 필요한 프로그램에 다시 할당할 수 없습니다. 물건을 보관하고 캐비닛을 잠근 후 열쇠를 분실하거나 열쇠를 반납하지 않으면 캐비닛을 재활용할 수 없습니다. 그에 대한 정보를 찾을 수 없기 때문에 재활용합니다.

  • 메모리 오버플로: 다양한 방법을 시도한 결과 접시에 과일이 4개밖에 담기지 못했는데 5개를 넣었는데 땅에 떨어져서 먹을 수 없었습니다. 이것은 오버플로입니다. 예를 들어, 스택이 가득 찼을 때 스택이 푸시되면 오버플로라고 하는 공간 오버플로가 발생하고, 스택이 비어 있을 때 스택을 다시 푸시하면 언더플로라고 하는 공간 오버플로가 발생합니다. 즉, 할당된 메모리가 일련의 데이터 항목을 보유하기에 충분하지 않으며 이를 메모리 오버플로라고 합니다. 직설적으로 말하면 그 정도는 참을 수 없어서 오류 신고를 하게 됩니다.

4. 메모리 누수 분류(발생 방식에 따라 분류)

  • 일반적인 메모리 누수. 메모리 누수가 있는 코드는 여러 번 실행되므로 실행될 때마다 메모리 누수가 발생합니다.

  • 가끔 메모리 누수가 발생합니다. 메모리 누수를 일으키는 코드는 특정 상황이나 작업에서만 발생합니다. 자주 발생하는 것과 산발적으로 발생하는 것은 상대적입니다. 특정 상황에서는 가끔씩 발생하는 일이 일반화될 수 있습니다. 따라서 테스트 환경과 테스트 방법은 메모리 누수를 감지하는 데 중요합니다.

  • 일회성 메모리 누수. 메모리 누수를 일으키는 코드는 한 번만 실행됩니다. 또는 알고리즘 결함으로 인해 항상 단 하나의 메모리 블록만 누수됩니다. 예를 들어, 클래스의 생성자에서 메모리가 할당되었지만 소멸자에서 메모리가 해제되지 않은 경우 메모리 누수는 한 번만 발생합니다.

  • 암시적 메모리 누수. 프로그램은 실행되는 동안 지속적으로 메모리를 할당하지만, 종료될 때까지 메모리를 해제하지 않습니다. 엄밀히 말하면 프로그램이 결국 요청된 모든 메모리를 해제하기 때문에 여기에는 메모리 누수가 없습니다. 그러나 며칠, 몇 주, 심지어 몇 달 동안 실행해야 하는 서버 프로그램의 경우 메모리를 제때 해제하지 못하면 결국 시스템 메모리가 모두 고갈될 수도 있습니다. 따라서 이러한 유형의 메모리 누수를 암시적 메모리 누수라고 부릅니다.

5. 메모리 오버플로 원인 및 해결 방법:

(1) 메모리 오버플로 원인:

  • 메모리에 로드되는 데이터의 양이 너무 많습니다.

  • 컬렉션 클래스에 객체에 대한 참조가 있어 사용 후 지워지지 않아 JVM이 재활용할 수 없습니다.

  • 코드에 무한 루프가 있습니다. 또는 루프가 너무 많은 중복 객체 엔터티를 생성합니다.

  • 시작 매개변수 메모리 값이 너무 작게 설정되었습니다.

  • (2) 메모리 오버플로에 대한 해결 방법:

첫 번째 단계는 JVM 시작 매개변수를 수정하고 메모리를 직접 늘리는 것입니다. (-Xms 및 -Xmx 매개변수를 추가하는 것을 잊지 마세요.) 두 번째 단계는 오류 로그를 확인하여 "OutOfMemory" 오류 이전에 다른 예외나 오류가 있는지 확인하는 것입니다.

세 번째 단계는 코드를 살펴보고 분석하여 메모리 오버플로가 발생할 수 있는 위치를 찾는 것입니다.

다음 사항에 중점을 두세요:

  • 데이터베이스 쿼리에 모든 데이터를 한번에 가져오는 쿼리가 있는지 확인해보세요. 일반적으로 100,000개의 레코드를 한 번에 메모리로 가져오면 메모리 오버플로가 발생할 수 있습니다. 이 문제는 상대적으로 숨겨져 있습니다. 온라인으로 전환하기 전에는 데이터베이스에 데이터가 적고 문제가 발생할 가능성이 적었지만 온라인으로 전환한 후에는 데이터베이스에 데이터가 많아 단일 쿼리로 인해 메모리 오버플로가 발생할 수 있습니다. 따라서 데이터베이스 쿼리에 페이징을 사용해 보십시오.

  • 코드에 무한 루프나 재귀 호출이 있는지 확인하세요.

  • 새로운 객체 엔터티를 반복적으로 생성하는 대규모 루프가 있는지 확인하세요.

  • 데이터베이스 쿼리에 모든 데이터를 한번에 가져오는 쿼리가 있는지 확인해보세요. 일반적으로 100,000개의 레코드를 한 번에 메모리로 가져오면 메모리 오버플로가 발생할 수 있습니다. 이 문제는 상대적으로 숨겨져 있습니다. 온라인으로 전환하기 전에는 데이터베이스에 데이터가 적고 문제가 발생할 가능성이 적었지만 온라인으로 전환한 후에는 데이터베이스에 데이터가 많아 단일 쿼리로 인해 메모리 오버플로가 발생할 수 있습니다. 따라서 데이터베이스 쿼리에 페이징을 사용해 보십시오.

  • 사용 후 List, MAP 등 컬렉션 객체가 지워지지 않는지 확인하세요. List 및 MAP과 같은 컬렉션 개체에는 항상 개체에 대한 참조가 있으므로 이러한 개체는 GC에서 재활용될 수 없습니다.

네 번째 단계, 메모리 보기 도구를 사용하여 메모리 사용량을 동적으로 확인합니다.

JVM8 메모리 모델

메모리 오버플로에 대한 10가지 시나리오

JVM 런타임에는 먼저 클래스 로더가 필요합니다( classLoader )는 필요한 클래스의 바이트코드 파일을 로드합니다. 로딩 후 실행을 위해 실행 엔진으로 넘겨지며, 실행 과정에서 데이터를 저장하기 위한 일정 기간(CPU 및 메인 메모리와 유사)이 필요합니다. 이 메모리 공간의 할당과 해제 과정은 우리가 관심을 가져야 할 런타임 데이터 영역이다. 메모리 오버플로는 클래스 로더가 로드될 때 발생합니다. 메모리 오버플로는 OutOfMemoryError와 StackOverflowError라는 두 가지 범주로 나뉩니다. 다음은 메모리 오버플로 상황 10가지를 나열하고, 예제 코드를 통해 메모리 오버플로가 어떻게 발생하는지 설명합니다.

1.java 힙 메모리 오버플로

java.lang.OutOfMemoryError:Java 힙 공간 예외가 발생하면 힙 메모리 오버플로입니다.

1) 문제 설명

  • jvm 메모리 세트가 너무 작고, 객체에 필요한 메모리가 너무 커서, 객체 생성 시 공간을 할당할 때 이 예외가 발생합니다.

  • 트래픽/데이터 최대치에는 특정 수의 사용자 또는 특정 양의 데이터와 같이 애플리케이션 자체 처리에 특정 제한이 있습니다. 사용자 수 또는 데이터 양이 갑자기 급증하여 예상 임계값을 초과하면 피크 정지 이전에 정상적으로 실행 중이던 작업이 중지되고 java.lang.OutOfMemoryError: Java heap space error

2)이 발생합니다. 샘플 코드

다음 코드를 컴파일하고, 실행 시 jvm 매개 변수를 -Xms20m -Xmx20m으로 설정하세요

위의 예에서 요청이 5m의 메모리를 한 번만 할당하면 요청량이 작아지고 가비지 컬렉션이 발생합니다. 정상이고 오류가 없지만 일단 동시 발생하면 최대 메모리 값을 초과하여 메모리 오버플로가 발생합니다.

3. Solution

먼저 코드에 문제가 없으면 두 개의 jvm 매개변수 -Xms 및 -Xmx를 적절하게 조정하고 스트레스 테스트를 사용하여 이 두 매개변수를 조정하여 최적의 값을 얻을 수 있습니다.

둘째, 파일 업로드 및 데이터베이스에서 대규모 배치 가져오기와 같은 대규모 개체에 대한 응용 프로그램은 피해야 합니다. 이는 시스템의 정상적이고 안정적인 실행에 도움이 됩니다. .

마지막으로, 가비지 수집이 빨리 수행될수록 더 좋습니다. 그렇지 않으면 동시 요청 수가 많아지면 새로운 요청에 메모리를 할당할 수 없게 되어 쉽게 급증할 수 있습니다. 시스템.

2. Java 힙 메모리 누수

1), 문제 설명

Java의 메모리 누수는 일부 객체가 애플리케이션에서 더 이상 사용되지 않지만 가비지 수집에서 인식되지 않는 상황입니다. 따라서 이러한 사용되지 않은 개체는 여전히 Java 힙 공간에 무기한 존재합니다. 지속적인 누적은 결국 java.lang.OutOfMemoryError를 유발합니다.

2), 샘플 코드

위 코드를 실행할 때 순수 캐싱 솔루션이 기본 맵을 모든 키가 아닌 10,000개 요소로만 확장한다고 가정하면 문제 없이 영원히 실행될 것으로 기대할 수 있습니다. 해시맵. 그러나 실제로는 키 클래스가 해당 equals() 메서드를 재정의하지 않기 때문에 요소가 계속 추가됩니다.

시간이 지남에 따라 유출된 코드가 지속적으로 사용됨에 따라 "캐시된" 결과는 결국 많은 Java 힙 공간을 소비하게 됩니다. 누수된 메모리가 힙 영역에서 사용 가능한 모든 메모리를 채우면 가비지 수집이 이를 정리할 수 없습니다. java.lang.OutOfMemoryError.

3), 솔루션

해당 솔루션은 비교적 간단합니다. equals 메서드를 다시 작성하면 됩니다:

3. 가비지 수집 시간 초과 메모리 오버플로

1), 애플리케이션이 사용 가능한 모든 메모리를 소진할 때의 문제 설명, GC 오버헤드 제한이 초과되었습니다. 오류가 발생하고 GC가 이를 여러 번 지우는 데 실패하면 java.lang.OutOfMemoryError가 발생합니다. JVM이 거의 효과 없이 GC를 실행하는 데 많은 시간을 소비하고 전체 GC 프로세스가 제한을 초과하면 오류가 트리거됩니다(기본 jvm 구성 GC 시간이 98%를 초과하고 재활용된 힙 메모리가 2 미만임) %).

2), 샘플 코드

3), 솔루션

객체 수명주기를 줄이려면 최대한 빨리 가비지 수집을 수행하십시오.

4. 메타공간 메모리 오버플로

1), 문제 설명

메타공간 오버플로, 시스템에서 java.lang.OutOfMemoryError: Metaspace가 발생합니다. 이러한 비정상적인 문제가 발생하는 이유는 시스템에 코드가 많거나 타사 패키지를 많이 참조하거나 동적 코드 생성 및 클래스 로딩을 사용하여 메타 공간에서 메모리 공간이 커지기 때문입니다.

2), 샘플 코드

3), 솔루션

기본적으로 메타 공간의 크기는 로컬 메모리에 의해서만 제한됩니다. 그러나 전체 기계의 성능을 위해서는 이 항목을 최대한 설정하여 전체 기계의 서비스 종료를 초래하지 않도록 해야 합니다.

  • 다른 JVM 프로세스에 영향을 주지 않도록 매개변수 구성을 최적화하세요

-XX: MetaspaceSize, 초기 공간 크기에 도달하면 유형 언로드를 위해 가비지 수집이 트리거되고 GC는 값을 조정합니다. 해제 공간이 많으면 값을 적절히 낮추고, 해제되는 공간이 적다면 MaxMetaspaceSize를 초과하지 않으면 값을 적절하게 늘립니다.

-XX:MaxMetaspaceSize, 최대 공간은 기본적으로 제한이 없습니다.

위의 두 가지 크기 지정 옵션 외에도 GC 관련 속성 두 가지가 있습니다: -XX:MinMetaspaceFreeRatio, GC 후 최소 메타스페이스 남은 공간 용량의 백분율이 할당된 공간으로 인한 가비지 수집으로 줄어듭니다. -XX:MaxMetaspaceFreeRatio, GC 이후 최대 메타스페이스 남은 공간 용량의 백분율이 공간 확보로 인한 가비지 수집으로 줄어듭니다.

  • 타사 패키지 인용 시 주의사항

타사 패키지를 신중하게 선택하고 불필요한 패키지를 제거하세요. 이는 컴파일 및 패키징 속도를 향상시키는 데 도움이 될 뿐만 아니라 원격 배포 속도도 향상시키는 데 도움이 됩니다.

  • 동적으로 생성된 클래스를 사용하는 프레임워크에 주의하세요

동적으로 생성된 클래스를 많이 사용하는 프레임워크의 경우 스트레스 테스트를 수행하여 동적으로 생성된 클래스가 메모리 요구 사항을 초과하는지 확인하고 예외가 발생하는지 확인해야 합니다. .

5. 직접 메모리 오버플로

1) 문제 설명

ByteBuffer에서 할당Direct()를 사용할 때 사용됩니다. 많은 javaNIO(예: netty) 프레임워크는 java.lang과 같은 다른 메소드로 캡슐화됩니다. OutOfMemoryError: 이 문제가 발생하면 직접 버퍼 메모리 예외가 발생합니다.

ByteBuffer의 할당Direct 메서드를 삭제하지 않고 직간접적으로 사용하면 비슷한 문제가 발생합니다.

2), 샘플 코드

3), 솔루션

유사한 작업이 자주 발생하는 경우 -XX:MaxDirectMemorySize 매개변수 설정을 고려하고 시간에 맞춰 메모리를 지울 수 있습니다.

6. 스택 메모리 오버플로

1), 문제 설명

스레드가 Java 메소드를 실행할 때 JVM은 새로운 스택 프레임을 생성하여 스택의 맨 위로 푸시합니다. 이때, 새로운 스택 프레임은 현재 스택 프레임이 되며, 메소드가 실행되면 스택 프레임은 매개변수, 지역 변수, 중간 명령 및 기타 데이터를 저장하는 데 사용됩니다.

메서드가 자신을 재귀적으로 호출하면 새 메서드에서 생성된 데이터(새 스택 프레임으로도 이해될 수 있음)는 메서드가 자신을 호출할 때마다 데이터를 복사합니다. 현재 메소드를 스택에 푸시합니다. 따라서 각 재귀 수준에서는 새로운 스택 프레임을 생성해야 합니다. 결과적으로 재귀 호출로 인해 스택의 메모리가 점점 더 많이 소모됩니다. 재귀 호출 자체가 백만 번이면 백만 개의 스택 프레임이 생성됩니다. 이로 인해 스택 메모리 오버플로가 발생합니다.

2), 샘플 코드

3), 솔루션

실제로 프로그램에 재귀 호출이 있고 스택 오버플로가 발생하는 경우 -Xss 크기를 늘려 스택 메모리 오버플로 문제를 해결할 수 있습니다. 재귀 호출은 무한 루프의 형성을 방지합니다. 그렇지 않으면 스택 메모리 오버플로가 발생합니다.

7. 로컬 스레드 생성 시 메모리 오버플로

1) 문제 설명

스레드는 기본적으로 힙이 아닌 메모리 영역만 점유합니다. 즉, 이 오류는 힙이 아닌 영역을 제외하고 발생함을 나타냅니다. 힙, 스레드에 메모리 영역을 할당할 수 없습니다. 이는 메모리 자체가 부족하거나 힙 공간이 너무 크게 설정되어 남은 메모리가 많지 않고 스레드 자체가 메모리를 차지하기 때문입니다. 충분하지 않습니다.

2), 샘플 코드

3), 솔루션

먼저 운영 체제에 스레드 수 제한이 있는지 확인하십시오. 이것이 문제인 경우 스레드를 생성할 수 없습니다. 시스템의 최대 지원 수를 조정하십시오.

일상적인 개발에서는 최대 스레드 수를 제어할 수 있는지 확인하고 스레드 풀을 임의로 사용하지 마십시오. 무한히 성장할 수는 없습니다.

8. 스왑 영역을 넘어서는 메모리 오버플로

1), 문제 설명

Java 애플리케이션 시작 프로세스 중에 -Xmx 및 기타 유사한 시작 매개변수를 통해 지정된 필수 메모리를 제한할 수 있습니다. JVM에서 요청한 총 메모리가 사용 가능한 실제 메모리보다 크면 운영 체제는 메모리의 내용을 하드 디스크로 변환하기 시작합니다.

일반적으로 JVM은 스왑 공간 부족 오류를 발생시킵니다. 이는 애플리케이션이 JVM 기본 힙에서 메모리 할당을 요청하지 못하고 기본 힙이 곧 소진될 때 오류 메시지에 크기가 포함됨을 의미합니다. 할당 실패(바이트) 및 요청이 실패한 이유입니다.

2) 해결 방법

시스템 스왑 영역의 크기를 늘리십시오. 개인적으로 스왑 영역을 사용하면 성능이 크게 저하될 것이라고 생각합니다. 프로덕션 환경에서는 이 방법을 최대한 피하는 것이 좋습니다. 시스템의 물리적 메모리를 초과하는 메모리. 둘째, 시스템 스왑 영역을 제거하고 시스템 메모리만 사용하여 애플리케이션 성능을 보장합니다.

9. 배열 초과 제한 메모리 오버플로

1) 문제 설명 때때로 요청된 배열 크기가 VM 제한을 초과한다는 설명이 표시됩니다. 최대 크기에는 제한이 있으며 플랫폼에 따라 다르지만 일반적으로 요소 수는 10억~21억 개입니다. 요청한 배열 크기가 VM 제한 오류를 초과하는 경우 이는 애플리케이션이 JVM(Java Virtual Machine)이 지원할 수 있는 것보다 큰 배열을 할당하려고 시도하고 있음을 의미합니다. JVM은 배열에 메모리를 할당하기 전에 플랫폼별 검사(할당된 데이터 구조가 이 플랫폼에서 주소 지정이 가능한지 여부)를 수행합니다.

2), 샘플 코드

다음은 배열이 최대 한도를 초과한 코드입니다.

3), 솔루션

따라서 배열 길이는 플랫폼에서 허용하는 길이 범위 내에 있어야 합니다. 그러나 이 오류는 일반적으로 Java 배열의 인덱스가 int 유형이기 때문에 거의 발생하지 않습니다. Java에서 가장 큰 양의 정수는 2^31 - 1 = 2,147,483,647입니다. 플랫폼별 제한은 이 숫자에 매우 가까울 수 있습니다. 예를 들어 내 환경(64비트 macOS, Jdk1.8 실행)에서는 최대 길이가 2,147,483,645(Integer.MAX_VALUE-2)인 배열을 초기화할 수 있습니다. 배열의 길이가 1씩 증가하여 nteger.MAX_VALUE-1에 도달하면 OutOfMemoryError가 발생합니다.

10. 시스템이 프로세스 메모리 오버플로를 종료합니다

1) 문제 개요 이 문제를 설명하기 전에 먼저 몇 가지 운영 체제 지식을 익히십시오. 운영 체제는 프로세스 개념을 기반으로 하며 이러한 프로세스는 커널 작업에는 "Out of memory killer"라는 아주 특별한 프로세스가 있습니다. 커널이 시스템 메모리가 부족하다는 것을 감지하면 OOM 킬러가 활성화되어 현재 누가 가장 많은 메모리를 차지하고 있는지 확인한 다음 프로세스를 종료합니다.

일반적으로 전체 운영 체제가 위험할 정도로 사용 가능한 가상 가상 메모리(스왑 공간 포함)가 소모되면 메모리 부족: 프로세스 종료 또는 자식 희생 오류가 발생합니다. 이 경우 OOM Killer는 "불량 프로세스"를 선택하고 종료합니다.

2), 샘플 코드

3), 솔루션

스왑 공간을 늘리면 Java 힙 공간 예외가 완화될 수 있지만 최선의 솔루션은 Java 애플리케이션이 메모리가 충분하면 이 문제가 발생하지 않습니다.

요약하자면, 위의 10가지 유형의 메모리 오버플로 상황을 통해 실제로 문제가 발생했을 때 문제를 해결하는 방법을 누구나 알게 될 것입니다.

  • 타사 jar 패키지를 도입해야 합니다. 컴파일 속도와 시스템 메모리 사용량을 향상시키기 위해 쓸모없는 jar 패키지를 신중하게 제거했습니다.

  • 대형 객체나 대용량 메모리 애플리케이션의 경우 최적화를 수행해야 합니다. 처리 성능을 향상하고 객체 수명주기를 줄이기 위해 대형 객체를 슬라이스로 처리해야 합니다.

  • 스레드가 차지하는 메모리를 제어할 수 있도록 스레드 수를 수정해 보세요. 많은 수의 스레드가 필요한 경우 운영 체제의 최대 열린 연결 수를 최적화해야 합니다.

  • 재귀 호출의 경우 재귀 수준도 제어해야 하며 너무 높거나 스택 깊이를 초과하지 않아야 합니다.

  • 스택에 할당된 메모리는 클수록 좋지 않습니다. 스택 메모리가 크고 스레드가 많을수록 힙을 위한 공간이 많이 남지 않고 OOM이 발생하기 쉽습니다. 일반적으로 JVM의 기본 매개변수(재귀 포함)에는 문제가 없습니다.

추천 관련 비디오 튜토리얼: Java 비디오 튜토리얼

위 내용은 Java에서 메모리 누수 및 메모리 오버플로란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.