동료에게 Java 객체 생성에서 수행 할 수있는 가장 비싼 작업이라고 들었습니다. 따라서 가능한 한 적은 수의 객체를 만드는 것으로 결론을 내릴 수 있습니다.
이것은 객체 지향 프로그래밍의 목적을 다소 어기는 것 같습니다. 객체를 만들지 않으면 최적화를 위해 하나의 긴 클래스 C 스타일을 작성하고 있습니까?
동료에게 Java 객체 생성에서 수행 할 수있는 가장 비싼 작업이라고 들었습니다. 따라서 가능한 한 적은 수의 객체를 만드는 것으로 결론을 내릴 수 있습니다.
이것은 객체 지향 프로그래밍의 목적을 다소 어기는 것 같습니다. 객체를 만들지 않으면 최적화를 위해 하나의 긴 클래스 C 스타일을 작성하고 있습니까?
답변:
가장 비싼 작업은 듣고 있습니다 . 그들은 당신에게 시간을 낭비하고 (이 답변이 게시 된 날짜 기준으로) 10 년이 지난 정보에 오도하고 여기에 게시하고 진실을 위해 인터넷을 연구하는 데 시간을 소비해야합니다.
그들이 10 년 전에 읽거나 읽은 것을 무지하게 역류시키고 더 이상 잘 모를 수 있기를 바랍니다. 나는 그들이 의심하는 다른 것을 취할 것입니다. 이것은 어느 쪽이든 최신 상태를 유지하는 사람에게는 잘 알려진 오류입니다.
primitives
)기본 요소 ( int, long, double
등) 이외의 모든 것은 Java의 오브젝트입니다. Java에서 오브젝트 작성을 피할 수있는 방법은 없습니다.
대부분의 경우 메모리 할당 전략으로 인해 Java로 객체를 생성하는 것이 C ++보다 빠르며, 모든 실제적인 목적으로 JVM의 다른 모든 것에 비해 "무료" 로 간주 될 수 있습니다 .
1990 년대 후반 2000 년대 초반 JVM 구현은 실제 객체 할당에서 약간의 성능 오버 헤드를 가졌습니다. 2005 년 이후로는 그렇지 않았습니다.
-Xms
응용 프로그램을 올바르게 실행하는 데 필요한 모든 메모리를 지원하도록 조정 하면 GC가 최신 GC 구현에서 대부분의 가비지를 실행하고 스윕 할 필요가 없으며 수명이 짧은 프로그램은 GC를 전혀 사용하지 않을 수 있습니다.
어쨌든 빨간색 청어 인 여유 공간을 사용하려고 시도하지 않고 런타임 성능을 최대화합니다. 즉, JVM 힙이 항상 거의 100 % 할당됨을 의미합니다. 여유 JVM 힙 메모리는 어쨌든 앉아있는 것을 제공하지 않습니다.
GC가 유용한 방법으로 나머지 시스템에 메모리를 다시 확보 할 것이라는 오해가 있습니다. 이것은 완전히 잘못된 것입니다!
JVM 힙은 커지거나 줄어들지 않으므로 나머지 시스템은 JVM 힙의 여유 메모리 에 의해 긍정적 인 영향을받습니다 . -Xms
시작시 지정된 모든 것을 할당하며 휴리스틱은 해당 메모리 인스턴스를 실제로 OS로 다시 릴리스하여 JVM 인스턴스가 완전히 종료 될 때까지 다른 OS 프로세스와 공유되도록하는 것입니다. -Xms=1GB -Xmx=1GB
주어진 시간에 실제로 생성되는 객체 수에 관계없이 1GB의 RAM을 할당합니다. 힙 메모리의 백분율을 해제 할 수있는 몇 가지 설정이 있지만 모든 실제적인 목적 으로 JVM은이 메모리를 충분히 릴리스 할 수 없습니다.따라서 다른 프로세스가이 메모리를 회수 할 수 없으므로 나머지 시스템은 JVM 힙이 해제 되어도 이점이 없습니다. 이에 대한 RFE는 2006 년 11 월 29 일 에 "수락" 되었지만 그에 대한 조치는 없었습니다. 이것은 권한이있는 사람에게는 문제가되지 않습니다.
수명이 짧은 작은 객체를 많이 만들면 JVM이 오랫동안 일시 중지된다는 잘못된 생각이 있습니다.
현재 GC 알고리즘은 실제로 수명이 짧은 많은 작은 객체를 만드는 데 최적화 되어 있으며 기본적으로 모든 프로그램에서 Java 객체의 99 % 휴리스틱입니다. 객체 풀링 을 시도 하면 실제로 대부분의 경우 JVM 성능이 저하됩니다.
오늘날 풀링이 필요한 유일한 객체 는 JVM 외부의 유한 리소스를 참조하는 객체입니다 . 소켓, 파일, 데이터베이스 연결 등을 재사용 할 수 있습니다. 일반 객체는 메모리 위치에 직접 액세스 할 수있는 언어와 같은 의미로 풀링 할 수 없습니다 . 객체 캐싱 은 다른 개념이며 일부 사람들이 순진하게 풀링 이라고 부르는 것일 수도 있고 아닐 수도 있습니다 . 두 개념은 같지 않으므로 혼동해서는 안됩니다.
최신 GC 알고리즘은 일정에 할당을 해제하지 않고 특정 세대에 여유 메모리가 필요할 때 할당을 해제하기 때문에이 문제가 없습니다. 힙이 충분히 큰 경우 일시 중지를 유발할만큼 오랫동안 할당 해제가 발생하지 않습니다.
_alloca
, 할부 상환 보다 빠르게 수행되는 메모리 아레나 클래스를 작성할 수 있습니다 .
new
핫스팟 에서 키워드를 사용하기 전에 항상 생성자에서 발생하는 일을 살펴 봐야 합니다. 사람들 이 Swing 객체 new ImageIcon(Image)
의 paint()
메소드 에서 사용하는 것을 보았습니다. 꽤 비싸고 전체 UI를 매우 부진하게 만들고있었습니다. 따라서 흑백 답변이 아닙니다 new
. 어딘가에서 사용하기 전에 생각하십시오 .
결론 : 객체를 생성하는 지름길을 취하기 위해 디자인을 손상시키지 마십시오. 불필요하게 객체를 만들지 마십시오. 합리적인 경우 중복 작업 (모든 종류)을 피하도록 설계하십시오.
대부분의 답변과 달리-객체 할당 DOES에는 관련 비용이 있습니다. 저렴한 비용이지만 불필요한 객체를 만들지 않아야합니다. 코드에서 불필요한 것을 피해야하는 것과 같습니다. 큰 객체 그래프는 GC를 느리게 만들고, 더 많은 메소드 호출이 필요하고, CPU 캐시 누락이 더 많이 발생하며, RAM이 적은 경우 프로세스가 디스크로 스왑 될 가능성이 높아짐에 따라 실행 시간이 더 길다는 것을 의미합니다.
누군가가 이것이 최첨단 사례임을 알리기 전에-최적화하기 전에 ~ 50 행의 데이터를 처리하기 위해 20 + MB의 객체를 만든 응용 프로그램을 프로파일 링했습니다. 분당 최대 100 개의 요청을 확장하고 갑자기 분당 2GB의 데이터를 생성 할 때까지 테스트에 적합합니다. 초당 20 회 요청을하려면 400MB의 객체를 생성 한 다음 버립니다. 괜찮은 서버의 경우 20 reqs / sec는 작습니다.
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
하는 수도 예를 들어 byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
.
실제로, Java 언어 (또는 다른 관리 언어)가 가능하게하는 메모리 관리 전략으로 인해 객체 생성은 어린 세대라고하는 메모리 블록에서 포인터를 증가시키는 것 이상입니다. 여유 메모리를 검색해야하는 C보다 훨씬 빠릅니다.
비용의 다른 부분은 객체 파괴이지만 C와 비교하기는 어렵습니다. 컬렉션 비용은 장기적으로 저장된 객체의 양을 기준으로하지만 콜렉션 빈도는 생성 된 객체의 양을 기준으로합니다. 결국 C 스타일 메모리 관리보다 훨씬 빠릅니다.
Point
오브젝트 2 개 범용 레지스터에 들어갈 수있다).
다른 포스터는 Java에서 객체 생성이 매우 빠르며 일반적인 Java 응용 프로그램에서는 일반적으로 걱정할 필요가 없다고 지적했습니다.
매우 특별한 상황의 몇 가지가 있습니다 입니다 객체 생성을 방지하는 것이 좋습니다.
동료의 말에 진실의 핵심이 있습니다. 나는 객체 생성 문제 가 실제로 가비지 수집 이라는 것을 정중하게 제안한다 . C ++에서 프로그래머는 메모리 할당 해제 방법을 정확하게 제어 할 수 있습니다. 프로그램은 원하는만큼 길거나 짧게 쌓일 수 있습니다. 또한 C ++ 프로그램은이를 생성 한 스레드와 다른 스레드를 사용하여 crud를 폐기 할 수 있습니다. 따라서 현재 작업중인 스레드는 정리를 중단 할 필요가 없습니다.
반대로 JVM (Java Virtual Machine)은 주기적으로 코드를 중지하여 사용하지 않는 메모리를 회수합니다. 대부분의 Java 개발자는이 일시 정지를 자주 인식하지 못합니다. 보통 일시 정지가 짧고 매우 짧기 때문입니다. JVM이 많이 쌓이거나 더 제한적 일수록 이러한 일시 정지가 더 자주 발생합니다. VisualVM 과 같은 도구 를 사용하여이 프로세스를 시각화 할 수 있습니다 .
최신 버전의 Java에서는 가비지 수집 (GC) 알고리즘 을 조정할 수 있습니다 . 일반적으로 일시 정지하려는 시간이 짧을수록 가상 머신의 오버 헤드가 더 비쌉니다 (예 : GC 프로세스를 조정하는 데 소비 된 CPU 및 메모리).
언제 문제가 될까요? 밀리 초 미만의 일관된 응답 속도에 관심이있을 때마다 GC가 중요합니다. Java로 작성된 자동화 된 거래 시스템은 일시 정지를 최소화하기 위해 JVM을 크게 조정합니다. 그렇지 않으면 Java를 작성하는 회사는 항상 시스템의 응답 성이 높은 상황에서 C ++로 전환합니다.
기록을 위해, 나는 일반적으로 객체 회피를 용납 하지 않습니다 ! 기본적으로 객체 지향 프로그래밍입니다. GC가 방해가되는 경우에만이 방법을 조정 한 다음 더 짧은 시간 동안 일시 중지하도록 JVM을 조정하려고 시도한 후에 만 조정하십시오. Java 성능 조정에 대한 좋은 책은 Charlie Hunt와 Binu John의 Java Performance 입니다.
오버 헤드로 인해 Java에서 너무 많은 오브젝트를 작성하지 않는 경우가 있습니다 . Android 플랫폼의 성능을 위해 설계되었습니다.
그 외에는 위의 답변이 사실입니다.
GC는 많은 단명 한 개체에 맞게 조정되었습니다.
즉, 객체 할당을 사소하게 줄일 수 있다면
하나의 예는 루프에서 문자열을 작성하는 것이며, 순진한 방법은
String str = "";
while(someCondition){
//...
str+= appendingString;
}
String
각 +=
작업 마다 새로운 객체 를 생성합니다 (그리고 a StringBuilder
와 새로운 기본 char 배열)
이것을 다음으로 쉽게 다시 작성할 수 있습니다.
StringBuilder strB = new StringBuilder();
while(someCondition){
//...
strB.append(appendingString);
}
String str = strB.toString();
이 패턴 (불변의 결과와 로컬의 변이 가능한 중간 값)은 다른 것들에도 적용될 수 있습니다
그 외에는 유령을 쫓는 대신 실제 병목 현상을 찾기 위해 프로파일 러를 끌어 야합니다.
StringBuilder
방법은입니다 사전 크기StringBuilder
그래서하는 결코 재 할당 와 기본 배열 StringBuilder(int)
생성자를. 이를 통해 할당이 아닌 단일1+N
할당이됩니다.
Joshua Bloch (Java 플랫폼 제작자 중 하나)는 2001 년 그의 책 Effective Java 에 썼습니다 .
풀의 오브젝트가 매우 무겁지 않은 경우 자체 오브젝트 풀을 유지하여 오브젝트 작성을 피하는 것은 좋지 않습니다. 객체 풀을 정당화하는 객체의 프로토 타입 예제는 데이터베이스 연결입니다. 연결 설정 비용이 충분히 높아서 이러한 오브젝트를 재사용하는 것이 합리적입니다. 그러나 일반적으로 자체 객체 풀을 유지 관리하면 코드가 복잡해지고 메모리 사용량이 증가하며 성능이 저하됩니다. 최신 JVM 구현에는 경량 객체에서 이러한 객체 풀을 쉽게 능가하는 최적화 된 가비지 수집기가 있습니다.
이것은 실제로 특정 응용 프로그램에 따라 다르므로 일반적으로 말하기가 어렵습니다. 그러나 객체 생성이 실제로 응용 프로그램의 성능 병목 현상 인 경우 놀랍습니다. 속도가 느리더라도 코드 스타일의 이점은 아마도 성능을 능가합니다 (실제로 사용자에게 눈에 띄지 않는 한)
어쨌든, 추측하는 대신 실제 성능 병목 현상을 결정하기 위해 코드를 프로파일 링 할 때까지 이러한 것들에 대해 걱정하지 않아도됩니다. 그때까지는 성능이 아니라 코드 가독성을 위해 최선을 다해야합니다.
동료가 불필요한 객체 생성의 관점에서 말한 것 같습니다. 동일한 객체를 자주 만드는 경우 해당 객체를 공유하는 것이 좋습니다. 객체 생성이 복잡하고 더 많은 메모리를 사용하는 경우에도 해당 객체를 복제하고 복잡한 객체 생성 프로세스를 만들지 않아도됩니다 (그러나 요구 사항에 따라 다름). 나는 "객체 생성이 비싸다"라는 문구를 맥락에서 취해야한다고 생각한다.
JVM 메모리 요구 사항과 관련하여 Java 8을 기다리면 -Xmx를 지정할 필요조차 없으며 메타 공간 설정이 JVM 메모리 요구를 처리하고 자동으로 커집니다.
메모리를 할당하는 것보다 클래스를 만드는 것이 더 많습니다. 초기화도 있습니다. 왜 그 부분이 모든 답변에 포함되지 않는지 잘 모르겠습니다. 일반 클래스에는 일부 변수가 포함되어 있으며 초기화 형식을 수행하며 무료가 아닙니다. 클래스가 무엇인지에 따라 파일을 읽거나 다른 수의 느린 작업을 수행 할 수 있습니다.
따라서 클래스 생성자가 사용 가능한지 여부를 파악하기 전에 클래스 생성자가 수행하는 작업을 고려하십시오.
Java의 GC는 실제로 "버스트"방식으로 많은 객체를 빠르게 생성하는 측면에서 매우 최적화되어 있습니다. 내가 이해할 수있는 것에서 그들은 객체가 지속되는 경우에만 메모리 공간에 "버스트 사이클"의 종류에 대해 순차적 할당 기 (가변 크기 요청에 가장 빠르고 간단한 O (1) 할당 자)를 사용합니다. GC주기가 끝나면 GC가 하나씩 수집 할 수있는 곳으로 이동합니다.
그렇기 때문에 실제 사용자 엔드 요구 사항으로 측정 된 것처럼 성능 요구가 충분히 중요 해지면 객체에 오버 헤드가 발생하지만 생성 / 할당 측면에서는 그렇게 많이 생각하지 않습니다. 그것은 반사와 동적 파견 (같은 개념을 지원하기 위해 필요한 참조의 지역과 자바에있는 모든 개체의 추가 용량과 더 가지고 Float
보다 큰 float
의 정렬 요구 사항 64 비트에 더 큰 4 번 같은 일이 자주, 그리고를 배열이 Float
반드시 내가 이해 한 것과 연속적으로 저장 되는 것은 아닙니다.)
필자가 Java에서 개발 한 것 중 가장 눈길을 끄는 것 중 하나는 내 필드 (VFX)의 헤비급 경쟁자가 인터랙티브 멀티 스레드 표준 경로 추적기 (방사선 캐싱 또는 BDPT 또는 MLS 또는 다른 것을 사용하지 않음)라고 생각하게 만들었습니다. CPU는 실시간 프리뷰를 제공하여 노이즈없는 이미지로 빠르게 수렴되었습니다. 나는 C ++의 전문가들과 함께 일하면서 어려움을 겪은 멋진 프로파일 러를 사용하여 경력을 쌓았습니다.
그러나 나는 소스 코드를 들여다 보았고 사소한 비용으로 많은 객체를 사용했지만 경로 추적기 (BVH 및 삼각형 및 재료)의 가장 중요한 부분은 객체를 매우 명확하고 고의로 피할 수 없었습니다. 주로 float[]
및 int[]
)를 사용하여 훨씬 적은 메모리를 사용하고 공간적 위치를 보장 float
하여 배열 에서 하나 에서 다음으로 이동했습니다. 저자가 박스형을 사용했다면Float
거기에는 성능에 다소 큰 비용이 듭니다. 그러나 우리는 그 엔진에서 가장 절대적으로 중요한 부분을 이야기하고 있으며, 개발자가 얼마나 능숙하게 최적화했는지를 감안할 때, 그는 다른 곳에서 객체를 행복하게 사용했기 때문에 그 측정을 최적화하고 매우 신중하게 적용했습니다. 그의 인상적인 실시간 경로 추적기에 사소한 비용.
동료에게 Java 객체 생성에서 수행 할 수있는 가장 비싼 작업이라고 들었습니다. 따라서 가능한 한 적은 수의 객체를 만드는 것으로 결론을 내릴 수 있습니다.
나만큼 성능이 중요하지 않은 분야에서도 중요하지 않은 장소에 자신을 햄스트링하면 효율적인 제품을 쓰지 않을 것입니다. 가장 성능이 중요한 분야는 생산성에 대한 요구가 더 높아질 수 있다고 주장합니다. 왜냐하면 우리는 그렇지 않은 일에 시간을 낭비하지 않고 정말로 중요한 핫스팟을 조정할 수있는 여분의 시간이 필요하기 때문입니다. . 위의 경로 추적기 예제와 마찬가지로 저자는 이러한 최적화를 능숙하고 신중하게 측정 한 후에 진정으로 진정으로 중요하고 아마도 후 시점에있는 곳에만 적용하고 다른 곳에서도 여전히 행복하게 사용하는 객체에만 적용했습니다.
float[]
대 Float[]
전자 순차적으로 백만을 처리하는 것은 상대적으로 매우 빠르게 두 번째보다 더 수 있습니다.
사람들이 자바에서 객체 생성은 큰 비용이 아니라고 말했지만 (추가 등과 같은 대부분의 간단한 작업보다 더 큽니다) 너무 피해서는 안됩니다.
그것은 여전히 비용이 든다고 말하고 때로는 가능한 많은 물건을 긁어 내려고 할 수도 있습니다. 그러나 프로파일 링 후에 만 이것이 문제라는 것을 보여주었습니다.
다음은 주제에 대한 훌륭한 프레젠테이션입니다. https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf
나는 이것에 대해 빠른 마이크로 벤치 마크 를 했고 github에 전체 소스 를 제공 했습니다. 필자의 결론은 객체를 만드는 것이 비싸거나 그렇지 않은 것이 문제가 아니라 GC가 처리해야 할 개념으로 객체를 지속적으로 만드는 것이 응용 프로그램이 GC 프로세스를 더 빨리 트리거하게한다는 것입니다. GC는 비용이 많이 드는 프로세스이므로 가능할 때마다 피하고 밀어 내려고하지 않는 것이 가장 좋습니다.