프로그래밍을 위한 광범위한 가능성을 열어주는 Java 작업의 선택은 근거가 있습니다. 이것은 Sun Microsystems(나중에 Oracle로 대체됨)에서 만든 조판된 고급 프로그래밍 언어로 상당히 유연한 구문을 사용합니다. 20년 이상 동안 Java는 세계 기술 시장에서 선도적인 위치를 차지하면서 인기를 유지해 왔습니다.
그 이점 중 하나는 특히 이 기능이 없는 C 및 C++ 언어와 비교할 때 Java의 자동화된 메모리 관리입니다. 내장된 가비지 수집기 기술인 «Garbage Collector» 또는 GC를 사용하여 수행됩니다.
[이 글의 출처 - https://hackernoon.com/how-to-detect-and-avoid-memory-leak-in-java]
물론 Java가 가비지 컬렉터와 같이 중요하고 유용한 기능을 가진 유일한 플랫폼은 아닙니다.
그러나 HotSpot 옵티마이저 또는 뛰어난 이전 버전과의 호환성, 비교와 같은 다른 기능 외에도 Java는 확실히 다른 플랫폼 중에서 두드러집니다.
그렇다면 Java에 메모리 분배 및 정리라는 유용한 기능이 있다면 무엇이 문제일까요?

메모리 누수와 그 원인은 무엇입니까
직접 예제의 본질을 더 잘 이해하려면 Java 메모리 구조부터 시작해야 합니다.
1. 메모리 구조와 정리

Java 애플리케이션 데이터는 «Stack» 또는 «Heap»과 같은 공간 블록에 저장할 수 있습니다.
- 스택 메모리는 «힙» 요소 및 기본 값 유형에 대한 참조를 저장하는 저장소입니다.
- 힙에는 스택의 변수가 참조하는 동적 객체가 포함됩니다.

결과적으로 기본 설정에서 힙은 스택보다 훨씬 더 많은 메모리 공간을 차지할 수 있습니다. 그러나 이러한 설정은 스택 값을 늘린 후 스택 값 아래로 힙 크기를 설정하여 수동으로 변경할 수 있습니다.
스택에는 LIFO 원칙이 있습니다 (마지막에 들어가고 먼저 떠남). 요소 또는 기본 값을 참조하여 새 메서드가 호출될 때마다 스택 맨 위에서 메모리 블록이 해제됩니다.

스택과 달리 힙은 자체적으로 정리되지 않습니다. 따라서 시스템에는 가비지 수집기 기능이 절실히 필요합니다. 존재하지 않으면 힙 크기만 관리할 수 있습니다.

스택 변수를 포함할 수 있는 힙의 개체에 대한 참조는 해당 유형에 따라 시간이 지남에 따라 가비지 수집기에 적합한 대상이 됩니다. 그리고 힙 메모리에 붉은색 아이템이 나타나면 수집가가 조립할 수 있습니다.
메모리에 있는 가비지 항목의 예:

2. 메모리 누수
Java 메모리 누수는 GC가 사용되지 않는 요소를 시스템에 남겨둘 때 발생하는 일종의 오류입니다. 이는 스택에서 참조될 수 있는 일부 가비지 항목을 제거할 수 없기 때문에 발생할 수 있습니다.
누출의 전형적인 예:

이 누출은 시스템의 자원을 활용하는 능력과 전체 생산성에 부정적인 영향을 미칩니다. 이 문제를 무시하면 시스템이 데이터 저장 공간을 완전히 소진하고 «Java Out Of Memory Error»라는 돌이킬 수 없는 오류로 종료될 수 있습니다.
메모리 관리 도구를 사용하여 최적화하는 것이 좋습니다. 가장 관련성이 높은 것:
- HP의 OVO(OpenView Operations);
- Apache의 JMETER;
- IBM의 IBM Tivoli;
- TechRepublic의 JProbe 프로파일러.
3. Java 메모리 누수의 원인
Java의 메모리 누수는 클라우드에서 원치 않는 개체에 대한 참조를 유지하는 코드의 예기치 않은 오류로 인해 발생할 수 있습니다. 이러한 링크는 GC 기능을 차단합니다. 결과적으로 이러한 요소에서 유용하게 사용하지 않는 저장소를 정리할 수 없습니다.
Java 메모리 누수의 주요 원인:
- 무제한 캐싱
- 한 세션에서 파일 오버플로;
- 대체 운영 체제 페이지의 초과
- 사용자 데이터 시스템의 버그
- 요소를 삭제하지 않고 컬렉션에 삽입
- 재현 불가능한 청취 방법.
Java 및 해당 유형의 메모리 누수
다양한 유형의 누출이 가능합니다. 이들의 차이점은 발생 방식과 발생 원인에 따라 다릅니다.
가장 일반적인 누출 유형:
1. 정적 변수의 과도한 사용
Java에서 정적 필드의 수명 주기는 일반적으로 «ClassLoader» 기능을 사용한 가비지 수집을 고려하지 않고 애플리케이션 세션 시간에 해당합니다. 명령 실행 중 메모리 힙을 분석할 때 제어 지점 1과 2 사이의 메모리 증가를 관찰할 수 있습니다.

그러나 VisualVM의 3번 지점에서 메서드 «populateList()»를 중지합니다. 힙 저장소는 처리되지 않은 상태로 유지됩니다. 그러나 2행의 «정적»을 고려하지 않으면 메모리 값이 변경됩니다.

이 경우 «populateList()» 메서드로 조작한 후 개체에 대한 모든 참조가 비활성화되므로 수집기에 의해 힙 메모리가 정리됩니다.

2. 외부 참조를 통한 내부 클래스의 가용성
정적 클래스를 초기화하려면 항상 외부 클래스의 예제가 필요합니다. 각 비정적 기본 클래스에는 저장된 클래스에 대한 숨겨진 참조가 포함되어 있습니다. 내부 클래스 개체를 사용하는 경우 외부 클래스 개체를 닫아도 GC에서 수집하지 않습니다.
예를 들어 비정적 값을 포함하고 여러 체적 요소에 대한 참조가 있는 클래스를 가져옵니다. 이 경우 내부 클래스 개체 생성에는 다음과 같은 통계 지표가 있습니다.

내부 또는 익명 클래스의 값을 정적 값으로 진부하게 변경하면 메모리 값이 급격하게 변경됩니다. 이는 외부 개체에 대한 내부 참조 클래스의 내용으로 인해 발생할 수 있으므로 GC의 기본 기능을 차단합니다.

3. 앱에서 닫히지 않은 리소스
새 연결을 만들거나 스레드를 열 때마다 JVM은 항상 새 스레드 또는 연결을 위한 메모리 공간을 할당합니다. 이러한 연결에는 포괄적인 데이터베이스가 있는 세션 개체가 있을 수 있습니다.
이러한 리소스를 닫지 않은 상태로 두면 저장소가 차단될 위험이 있습니다. 이로 인해 가비지 수집기가 이러한 개체를 감지하고 제거하지 못할 위험이 있습니다. 리소스를 안정적이고 올바르게 닫는 유지 관리를 무시하면 «Java 메모리 부족 오류» 오류가 나타날 때까지 메모리를 가득 채울 것입니다.
4. finalize() 메서드 사용
체계에서 재정의된 finalize() 메서드가 있는 클래스가 있을 때 해당 개체는 제때 GC를 수집하지 못할 수 있지만 삭제를 위해 대기합니다. 또한 finalize()에서 잘못 재정의된 코드와 가비지 수집기와의 속도 불일치로 인해 «OutOfMemoryError» 오류가 발생할 수 있습니다.
예를 들어, 재정의된 메서드 finalize()가 있고 시간이 필요한 클래스를 예로 들어 보겠습니다.
GC에 의해 수집된 많은 수의 요소가 있는 경우 다음과 같은 힙 점수가 형성됩니다.

재정의된 finalize()를 간단히 제거하면 다음 값을 얻을 수 있습니다.

5. 인턴 문자열 사용
Java 시스템 버전 6에서는 볼륨 문자열 사용에 주의해야 했습니다. 버전 7은 PermGen에서 HeapSpace로 이동한 문자열 풀의 변경 사항을 이동했습니다.
큰 문자열을 읽기 위해 «intern()» 메서드를 호출하면 상수 메모리(PermGen)의 문자열 풀에 저장됩니다. 이 메서드는 세션이 끝날 때까지 PermGen에 저장되며 이로 인해 애플리케이션의 메모리가 부족해집니다.
영구 메모리의 예는 해당 국가가 없는 파일의 문자열을 읽을 때입니다.

6. ThreadLocals 참여
ThreadLocal – 변수 값을 닫아 스트림 보안을 생성하는 도구입니다. 동시에 모든 스레드에는 중복 ThreadLocal 변수에 대한 숨겨진 링크가 있으며 모든 스레드에서 리소스를 사용하는 대신 복사본을 저장합니다.
이 기능은 유용성 외에도 오류가 있습니다. 잘못 사용하면 누출에 영향을 미칠 수 있습니다.
ThreadLocal 변수는 이를 포함하는 스레드를 삭제한 후 GC에서 수집해야 합니다.
그러나 일부 시스템 서버에서는 이 기능 권한을 사용하지 못할 수 있습니다. 이는 서버가 모든 요청에 대해 새 스레드를 생성하지 않고 전체 스레드 풀을 적용하기 때문에 발생할 수 있습니다.
서버의 풀은 스레드를 재사용하여 가비지 수집기에 액세스 할 수 없도록 만들고 메모리를 차지합니다.
7. 잘못된 equals() 및 hashCode() 구현
새 클래스를 만들 때 equals() 및 hashCode() 메서드의 재정의 오류가 자주 발생할 수 있습니다. 이러한 메서드는 HashSet 및 HashMap에서 일반적으로 사용되며 재정의 오류가 포함된 경우 과도한 메모리 소비가 발생할 수 있습니다.
예를 들어 equals() 및 hashCode() 메서드를 사용하여 항목을 처리하고 캐시에 저장하는 ORM Hibernate를 사용할 수 있습니다. 이러한 메서드가 재정의되지 않은 경우 Hibernate는 항목을 분석하지 않고 캐시가 해당 복사본을 채워 메모리 누수를 일으킵니다.
증상 및 Java 메모리 누수 감지
1. 메모리 누수 증상
누출을 나타낼 수 있는 몇 가지 의심스러운 점이 있습니다.
- 지속적이고 예상치 못한 시스템 장애;
- 불안정한 애플리케이션 기능 지원
- 긴 세션 중에 «Java.lang.OutOfMemoryError» 오류가 발생했습니다.
- 시스템에 의한 연결 객체 제거.
- 전체 시스템 성능이 크게 저하됩니다.
2. Java 메모리 누수 감지 방법
누출을 감지하려면 몇 가지 도구와 기술 및 이들의 조합이 필요합니다. 신뢰할 수 있는 방법 목록이 있습니다.
2.1. 메모리 프로파일러 : 리포지토리에서 공간을 차지하는 파일 및 항목을 추적하는 도구입니다. 누출을 감지하고 시스템에 사용되는 요소의 올바른 분포를 분석할 수 있습니다. 또한 처리하는 데 걸리는 시간을 추정하십시오.
Java 메모리 프로파일링을 위한 가장 일반적인 도구:
- EJ-Technologies의 JProfiler;
- Oracle Corporation의 Java VisualVM;
- YourKit by Yourkit GmbH.
2.2. 인게이지먼트 힙 덤프: Java 메모리 저장소에서 힙의 즉각적이고 시기 적절한 스냅숏을 생성하기 위한 도구입니다. 이러한 이미지는 사용되는 개체 수와 메모리에서 해당 개체의 가중치를 제어하는 데 필요합니다. 또한 이 도구는 시스템에서 생성된 요소의 수와 누수에 영향을 미칠 수 있는 요소를 추적합니다.
2.3. 자세한 가비지 수집 로그를 활성화합니다. 이 도구는 리포지토리 및 GC의 힙 구성에 대한 변경 사항을 보여줄 수 있습니다. 애플리케이션의 가장 정확한 기능과 성능을 제공합니다. 그리고 힙에서 적합한 요소, 대체 방법 및 JVM 매개변수를 식별하여 수집기의 성능을 최적화합니다.
예를 들어 JVM 시작 방법을 사용하여 자세한 정보 수집 인앱을 활성화합니다.
«-XX: +UseSerialGC -Xms1024m -Xmx1024 m -verbose:gc»
«-verbose:gc» 인수는 수집 정보 기록을 활성화합니다. 기본적으로 로그는 stdout에 저장되고 모든 GC에 대한 행을 생성합니다. 또한 «-XX: +UseSerialGC» 인수를 사용하여 순차적 GC를 지정하고 힙 크기를 설정합니다.
이는 적시에 누출을 감지하고 애플리케이션 상태 지표를 구성하는 데 도움이 될 수 있습니다.
GC 로그 활성화의 예는 GitHub 서비스에서 찾을 수 있습니다.
메모리 누수를 수정하는 방법. 오류 수정 방법
이 문제를 해결하기 위해서는 먼저 문제가 발생한 이유를 고려해야 합니다. 따라서 누수 유형을 파악하고 그 다양성에 따른 문제 해결이 필요하다.
각 유형마다 Java에서 메모리 오류를 수정하는 방법이 다릅니다.

1. 메모리 오류는 어떻게 해결하나요?
1.1. 정적 변수를 사용하는 경우:
시스템에서 정적 필드 사용을 최소화합니다. 이 경우 개체를 긴급하게 로드하는 대신 «게으른» 항목을 사용할 수 있습니다.
1.2. 외부를 참조하는 내부 클래스가 있는 경우:
외부 클래스 요소가 필요하지 않은 경우 내부 클래스를 정적 클래스로 변환할 수 있습니다.
1.3. 애플리케이션의 리소스가 닫히지 않은 경우:
리소스 사용을 완료하려면 «finally»를 적시에 활성화해야 합니다.
1.4. finalize() 메서드를 사용할 때:
결선 진출자와의 모든 작업을 0으로 줄여야 합니다.
1.5. 인턴 문자열을 사용하는 경우
Java 앱을 최신 버전으로 업그레이드하십시오. 이것은 6번째 버전 이후 문자열 풀을 힙의 빈 위치로 이동하여 작동할 수 있습니다. 또한 «메모리 부족 오류» 오류를 방지하기 위해 대량 문자열 작업에서 «PermGen» 크기를 확장할 수 있습니다.
1.6. ThreadLocal을 사용하는 경우
필요하지 않을 때 ThreadLocal 변수를 안정적으로 정리합니다. remove() 속성이 있는 ThreadLocal – 모든 현재 스레드에 대한 변수 값을 제거합니다. 비활성화되도록 하려면 «finally» 블록에서 닫아야 합니다.
1.7. 잘못된 equals() 및 hashCode() 구현 시
새 요소를 만들 때 equals() 및 hashCode() 경로를 재정의하는 것이 항상 최적입니다.
2. 메모리 누수를 제거하는 다른 방법
발생 원인을 명확하게 이해하지 못하는 경우 메모리 누수를 방지하기 위해 다른 방법을 적용할 수도 있습니다.
2.1. 벤치마킹 사용:
Java의 코드 성능에 대한 자세한 분석을 위해 벤치마킹과 함께 테스트를 사용할 수 있습니다.
이러한 방식으로 효율성을 비교하여 작업을 수행하는 다양한 방법의 생산성을 쉽게 평가할 수 있습니다. 이렇게 하면 불필요한 메모리 소비를 피할 수 있는 모범 사례가 우선적으로 적용됩니다.
2.2. 참조 객체 앱:
응용 프로그램의 요소에 대한 직접 참조 대신 Java의 특수 참조 개체를 적용하는 옵션입니다. «java.lang.ref» 패키지에 포함된 이러한 링크를 사용하면 가비지 수집기에 의해 불필요한 개체를 간단히 제거할 수 있습니다.
2.3. 코드 검토:
진부하지만 경우에 따라 덜 효과적인 방법은 코드를 확인하는 것입니다. 때로는 메모리 누수를 제거하는 데 한 번 도움이 됩니다.
2.4. 프로파일링 활성화:
위의 방법을 사용하여 응용 리소스를 저장하는 데 유용하게 사용할 수 있는 저장소 영역을 찾을 수 있습니다.
2.5. 자세한 가비지 수집:
앞서 언급한 또 다른 방법. 이 모드를 포함하면 GC 작업을 면밀히 모니터링할 수 있습니다.
이러한 저장 공간의 규칙을 준수하고 합리적으로 사용하면 Java가 프로그래밍을 백업하고 Java가 언어를 백업하는 상황을 제거하는 데 도움이 될 수 있습니다. 이는 시스템 전체의 정상적인 작동을 위한 기초입니다.
3. 도구 제거 오류:
- Quest Foglight for Java(힙 모니터링용),
- MAT(힙 덤프 분석용).
Java 보고서 작업 전문가로서 포털 Habr 에서 – 인용문:
" 누수를 찾아 수정한 후 모든 단계를 다시 수행하고 메모리를 분석하고 메모리 분석기에 보고하여 수정이 도움이 되었는지 확인하는 것은 불필요한 일이 아닙니다."
결론
따라서 Java에서 메모리 누수란 무엇인가라는 질문을 분석하면 이것이 시스템을 공격하고 전반적인 상태를 악화시키는 질병으로 시스템에 영향을 미친다는 것이 분명해집니다. 치료하지 않으면 돌이킬 수 없는 결과를 초래할 수 있습니다. 그리고 이것은 매우 심각한 문제이기 때문에 처음에는 눈에 띄지 않지만 적시에 식별하고 해결해야 합니다.
그 존재의 첫 징후가 나타난 지 오랜 시간이 지난 후에 문제가 감지되면 문제를 쉽게 해결할 수 없습니다. Java 프로그래밍의 고도의 자격을 갖춘 전문가의 작업을 포함하는 경우에도 누수를 한 번에 해결할 «마법의 알약»은 없습니다.
그러나 앱 작업에 입증된 방법, 프로파일링, 추적, Java 메모리 관리 및 코드 확인이 일관되게 포함된 경우 이러한 문제의 출현을 0으로 줄일 수 있습니다.
[출처 - https://hackernoon.com/how-to-detect-and-avoid-memory-leak-in-java]
'테크' 카테고리의 다른 글
JavaScript란 무엇인가, 무엇을 할 수 있는가? (0) | 2023.03.03 |
---|---|
(5) 최고의 Java 프로그래밍 책 6권 (2) | 2023.03.01 |
(3) Java를 사용하는 기업, 사용자들에게 추천하는 프로젝트 (0) | 2023.02.28 |
(2)Java와 다른 언어들 차이점, 유사점 (0) | 2023.02.28 |
Java란 무엇인가, 왜 사용하는가 (0) | 2023.02.28 |