작업 이후 점점 더 많은 코드가 작성되었으며 프로그램이 점점 더 부풀어 오르고 효율성이 점점 줄어들 었습니다. 이것은 완벽을 추구하는 나와 같은 프로그래머에게는 절대 허용되지 않습니다. 따라서 프로그램 구조를 지속적으로 최적화하는 것 외에도 메모리 최적화 및 성능 튜닝이 일반적인 "트릭"이되었습니다.
Java 프로그램 메모리 및 성능을 최적화하고 조정하려면 가상 머신의 내부 원리 (또는보다 엄격한 사양)를 이해하지 못하는 것은 불가능합니다. 다음은 "심층적 인 Java Virtual Machine (Second Edition)"(Cao Xiaogang과 Jiang Jing이 번역 한 Bill Venners에 의해 좋은 책입니다. 실제로이 기사는이 책을 읽은 후 Java Virtual Machines에 대한 저자의 개인적인 이해입니다). 물론 Java 가상 머신을 이해하는 이점은 위의 두 가지 이점에 국한되지 않습니다. 더 깊은 기술적 인 관점에서 Java Virtual Machines의 사양 및 구현을 이해하면 효율적이고 안정적인 Java 코드를 작성하는 데 도움이됩니다. 예를 들어, Java Virtual Machine의 메모리 모델과 가상 머신의 메모리 재활용 메커니즘을 이해하면 너무 많이 의존하지는 않지만 필요할 때 "메모리를 릴리스"할 것입니다 (Java 코드는 메모리를 명시 적으로 릴리스 할 수 없지만 객체가 객체를 해방하여 재활용해야한다는 사실을 알 수 있습니다. Java 스택의 작동 방식을 이해하면 재귀 층 수와 루프 수를 줄임으로써 스택 오버플로 위험을 줄일 수 있습니다. 응용 프로그램 개발자의 경우 이러한 Java 가상 머신의 기본 구현 작업을 직접 포함하지는 않지만이 배경 지식을 이해하면 우리가 작성하는 프로그램에 미묘하고 좋은 영향을 미칩니다.
이 기사는 Java Virtual Machine의 아키텍처 및 메모리 모델을 간략하게 설명합니다. 부적절한 단어 나 부정확 한 설명이 있으면 수정하십시오. 나는 매우 영광입니다!
Java 가상 기계 아키텍처
클래스 로딩 서브 시스템
Java Virtual Machines의 2 개의 클래스 로더, 즉 스타트 업 클래스 로더 및 사용자 정의 로더가 있습니다.
클래스로드 서브 시스템은 클래스의 완전히 자격있는 이름 (패키지 이름 및 클래스 이름, 네트워크 마운트도 URL 포함)을 통해 클래스를 런타임 데이터 영역에로드합니다. 로드 된 각 유형에 대해 Java Virtual Machine은 Java.lang.class 클래스의 인스턴스를 생성하여 메모리의 힙 영역에 배치되는 유형을 나타내며로드 된 유형 정보는 다른 모든 개체와 동일한 방법 영역에 있습니다.
유형을로드하기 전에 클래스로드 하위 시스템은 해당 바이너리 클래스 파일을 찾아 가져 오는 것뿐만 아니라 가져온 클래스의 정확성을 확인하고 클래스 변수에 대한 메모리를 할당하고 초기화하고 기호 참조를 직접 참조로 구문 분석해야합니다. 이러한 작업은 다음 순서로 엄격하게 있습니다.
1) 로딩 - 유형의 이진 데이터를 찾고로드;
2) 연결 - 검증, 준비 및 구문 분석 수행 (선택 사항)
3) 가져온 유형의 정확성을 확인하십시오.
4) 클래스 변수에 대한 메모리를 할당 준비하고 기본값으로 초기화하십시오.
5) 직접 응용 프로그램에 유형의 기호 참조를 분석합니다.
방법 영역
클래스로드 서브 시스템에서로드 된 각 유형에 대해 가상 머신은 다음 데이터를 메소드 영역에 저장합니다.
1. 완전히 자격을 갖춘 유형 이름
2. 유형의 자격을 갖춘 유형 슈퍼 클래스 (java.lang.object는 슈퍼 클래스가 없습니다)
3. 유형 A 클래스 유형 또는 인터페이스 유형입니다.
4. 액세스 수정자를 입력하십시오
5. 직접 하이퍼 interface의 완전히 자격을 갖춘 이름 주문 목록
위의 기본 유형 정보 외에도 다음 정보도 저장됩니다.
6. 상수 풀을 입력하십시오
7. 필드 정보 (필드 이름, 필드 유형, 필드 수정 자 포함)
8. 메소드 정보 (메소드 이름, 반환 유형, 번호 및 유형, 매개 변수, 메소드 수정 자 포함. 메소드가 추상적이고 로컬이 아닌 경우 메소드 바이트 코드, 피연산자 스택 및 메소드 스택 프레임의 로컬 변수 영역의 크기 및 예외 테이블도 저장됩니다)
9. 상수를 제외한 모든 클래스 변수 (실제로는 클래스의 정적 변수입니다. 정적 변수는 모든 인스턴스에서 공유되고 유형과 직접 관련되기 때문에 클래스 수준 변수이며 메소드 영역에서 클래스의 구성원으로 저장됩니다).
10. 클래스 로더에 대한 참조
// 반환 된 것은 클래스 로더 참조 string.class.getClassLoader ()입니다. 클래스 클래스에 대한 참조 // 참조 문자열을 반환합니다. 방금 저장된 클래스 클래스의 클래스 클래스;
방법 영역은 쓰레기 수집기에 의해 재활용 될 수 있습니다.
더미
런타임에 Java 프로그램에서 생성 된 모든 클래스 인스턴스 또는 배열은 동일한 힙에 배치되며 각 Java 가상 머신에는 힙 공간이 있으며 모든 스레드는 힙을 공유합니다 (이것은 멀티 스레드 Java 프로그램이 객체 액세스에서 동기화 문제를 일으키는 이유입니다).
각 Java Virtual Machine은 가상 머신 사양의 다양한 구현을 가지고 있으므로 각 Java 가상 머신은 힙의 객체 인스턴스를 나타내는 형태를 알 수 없지만 다음과 같은 구현을 통해 엿볼 수 있습니다.
프로그램 카운터
Java 프로그램을 실행하기 위해서는 각 스레드에는 자체 PC (프로그램 카운터) 레지스터가 있으며, 이는 스레드가 시작될 때 생성되고 한 단어의 크기로 작성되며 실행 해야하는 다음 코드 라인의 위치를 저장하는 데 사용됩니다.
자바 스택
각 스레드에는 자바 스택이있어 스레드의 실행 상태를 스택 프레임 단위로 저장합니다. Java 스택에는 가상 머신의 두 가지 유형이 있습니다 : 스택 프레스 및 스태킹 모두 프레임이 있습니다. 스택 프레임은 수신 매개 변수, 로컬 변수, 중간 작업 결과 등과 같은 데이터를 저장하며, 메소드가 완성 된 다음 해제 될 때 팝업됩니다.
두 개의 로컬 변수가 함께 추가 될 때 스택 프레임의 메모리 스냅 샷을 살펴보십시오.
로컬 메소드 스택
Java가 JNI (Java Native Interface, Java Local Interface)를 구현하는 데 사용되는 운영 체제 로컬 라이브러리를 호출하는 곳입니다.
실행 엔진
Java 가상 기계 제어의 핵심은 Java Bytecode 및 구문 분석을로드합니다. Java 프로그램을 실행하기 위해 각 스레드는 독립적 인 가상 머신 실행 엔진의 인스턴스입니다. 스레드 수명주기의 시작부터 바이트 코드를 실행하거나 로컬 메소드를 실행합니다.
로컬 인터페이스
로컬 메소드 스택 및 운영 체제 라이브러리에 연결되었습니다.
참고 :이 기사에 언급 된 모든 장소는 "Javaee 및 Javase 플랫폼의 Java Virtual Machine 사양"을 참조하십시오.
가상 머신 메모리 최적화 연습
메모리가 언급되면 메모리 누출이 언급되어야합니다. 우리 모두 알다시피, Java는 C ++의 기초에서 개발되었으며 C ++ 프로그램의 큰 문제는 메모리 누출을 해결하기가 어렵다는 것입니다. Java의 JVM에는 메모리를 재활용하기위한 자체 쓰레기 수집 메커니즘이 있지만, 많은 경우 Java 프로그램 개발자는 너무 걱정할 필요가 없지만 C ++보다 약간 작은 누출 문제가 있습니다. 예를 들어, 프로그램에는 참조되었지만 쓸모없는 객체가 있습니다. 프로그램이 객체를 참조하지만 향후에 사용하지 않거나 사용할 수없는 경우 메모리 공간이 낭비됩니다.
먼저 GC의 작동 방식 : 응용 프로그램, 인용, 인용, 할당 등을 포함하여 각 객체의 실행 상태를 모니터링하십시오. 개체가 더 이상 인용되지 않으면 개체를 해제하십시오 (GC의 초점은 너무 많이 설명되지 않습니다). 많은 Java 프로그래머는 GC에 너무 의존하지만 문제의 핵심은 JVM의 쓰레기 수집 메커니즘이 아무리 좋더라도 메모리는 항상 제한된 자원이라는 것입니다. 따라서 GC가 대부분의 쓰레기 수집을 완료하더라도 인코딩 프로세스 중에 적절하게 메모리 최적화에주의를 기울여야합니다. 이는 메모리 활용을 개선하고 프로그램 효율성을 극대화하면서 GC의 수를 효과적으로 줄일 수 있습니다.
전반적으로 Java 가상 머신의 메모리 최적화는 Java Virtual Machines와 Java 응용 프로그램의 두 가지 측면에서 시작해야합니다. 전자는 가상 머신의 메모리가 프로그램의 메모리 요구 사항을 보완 할 수 있도록 응용 프로그램의 설계에 따라 가상 머신 매개 변수를 통해 가상 머신 논리 메모리 파티션의 크기를 제어하는 것을 말합니다. 후자는 프로그램 알고리즘 최적화, GC 부담을 줄이며 GC 재활용의 성공률을 향상시키는 것을 말합니다.
매개 변수를 통해 가상 머신 메모리를 최적화하기위한 매개 변수는 다음과 같습니다.
XMS
초기 힙 크기
xmx
자바 힙 최대 값
1MN
젊은 세대의 힙 크기
XSS
각 스레드의 스택 크기
위의 것은 더 일반적으로 사용되는 세 가지 매개 변수이며 일부는 다음과 같습니다.
XX : MinHeapFreeratio = 40
확장을 피하기 위해 GC 후 힙이없는 최소 백분율.
XX : MaxHeapFreeratio = 70
수축을 피하기 위해 GC 후 힙이없는 최대 백분율.
XX : Newratio = 2
신규/구식 크기의 비율. [SPARC- 클라이언트 : 8; x86 -server : 8; x86- 클라이언트 : 12.] -클라이언트 : 8 (1.3.1+), x86 : 12]
XX : Newsize = 2.125m
새로운 세대의 기본 크기 (바이트) [5.0 및 최신 : 64 비트 VM은 30% 더 크게 스케일링됩니다. x86 : 1m; x86, 5.0 이상 : 640k]
xx : maxnewsize =
새로운 세대의 최대 크기 (바이트). 1.4 이후 MaxNewsize는 Newratio의 함수로 계산됩니다.
XX : Survivorratio = 25
에덴/생존자 공간 크기의 비율 [1.3.1 : 25의 SPARC; 5.0 이상의 다른 Solaris 플랫폼 : 32]
xx : permsize =
영구 생성의 초기 크기
XX : MaxPermsize = 64m
영구 생성의 크기. [5.0 및 최신 : 64 비트 VM은 30% 더 크게 스케일링됩니다. 1.4 AMD64 : 96m; 1.3.1 -Client : 32m.]
프로그램 알고리즘을 최적화하여 메모리 활용을 향상시키고 메모리 위험을 줄이기 위해 아래에 언급 된 것은 전적으로 경험적이며 참조 용입니다. 부적절한 것이 있다면, 저를 바로 잡으십시오. 감사합니다!
1. 가능한 한 빨리 쓸모없는 물체의 참조를 해제하십시오 (xx = null;)
코드를보십시오.
공개 목록 <PagedAta> 구문 분석 (htmlpage page) {list <pagedata> list = null; try {list valuelist = page.getByxpath (config.getContentXpath ()); if (valuelist == null || valuelist.isempty ()) {return list; } // 필요할 때 객체를 만들고 메모리를 저장하고 효율성을 향상시킵니다. 목록 = new ArrayList <PagedAta> (); pagedata pagedata = new pagedata (); StringBuilder value = new StringBuilder (); for (int i = 0; i <valuelist.size (); i ++) {htmlelement content = (htmlelement) valuelist.get (i); domnodelist <htmlelement> imgs = content.getElementsByTagName ( "IMG"); if (imgs! = null &&! imgs.isempty ()) {for (htmlelement img : imgs) {try {htmlimage image = (htmlimage) img; 문자열 path = image.getSrcattribute (); 문자열 형식 = path.substring (path.lastindexof ( "."), path.length ()); 문자열 localPath = "d :/images/" + md5helper.md5 (path) .replace ( "//", ","). REPLATE ( "/", ",") + 형식; 파일 LocalFile = 새 파일 (localPath); if (! localfile.exists ()) {localfile.createnewfile (); image.saveas (localfile); } image.setattribute ( "src", "file : ///" + localPath); localfile = null; image = null; img = null; } catch (예외 e) {}} //이 객체는 향후 사용되지 않습니다. 이에 대한 참조를 지우는 것은 GC에 미리 알리는 것과 같습니다. 객체는 imgs = null을 재활용 할 수 있습니다. } 문자열 text = content.asxml (); value.append (text) .append ( "<br/>"); valuelist = null; 내용 = null; 텍스트 = null; } pagedata.setContent (value.toString ()); pagedata.setcharset (page.getPageEncoding ()); list.add (pagedata); // pagedata = null; 목록이 여전히 객체에 대한 참조를 보유하고 있으며 GC는 값 = null을 재활용하지 않기 때문에 쓸모가 없습니다. // 여기에는 list = null이 없습니다. 목록은 메소드의 반환 값이기 때문에, 그렇지 않으면 메소드에서 얻는 리턴 값은 항상 비어 있으므로 이러한 종류의 오류는 발견되거나 배제하기 쉽지 않습니다} catch (예외 e) {} 리턴 목록; }2. 배열, 트리, 그래프, 링크 된 목록 및 기타 데이터 구조와 같은 수집 데이터 유형을 신중하게 사용하십시오. 이러한 데이터 구조는 GC의 재활용에 더 복잡합니다.
3. 배열 공간을 명시 적으로 적용하지 마십시오. 명시 적으로 적용 해야하는 경우 가능한 한 정확하게 합리적인 가치를 추정하십시오.
4. 클래스의 기본 생성자에서 많은 수의 객체를 생성하고 초기화하지 않도록하고, 클래스의 자체 생성자를 호출 할 때 불필요한 메모리 자원 낭비를 방지하십시오.
5. 강제 시스템이 쓰레기 메모리를 재활용하고 시스템에서 쓰레기 재활용의 마지막 시간을 늘리는 것을 피하십시오.
6. 원격 발신자가 즉각적인 값 변수의 값을 얻어야하지 않는 한 원격 메소드 통화 애플리케이션을 개발할 때 즉각적인 값 변수를 사용하십시오.
7. 시스템 성능을 향상시키기 위해 적절한 시나리오에서 객체 풀링 기술을 사용하십시오.