paint-brush
소프트웨어는 먼저 죽이지 않으면 성공한다: 성급한 최적화와 Java GC 이야기~에 의해@wasteofserver
549 판독값
549 판독값

소프트웨어는 먼저 죽이지 않으면 성공한다: 성급한 최적화와 Java GC 이야기

~에 의해 Frankie6m2024/04/06
Read on Terminal Reader

너무 오래; 읽다

최적화에 너무 집착하지 말고 언어가 당신에게 도움이 되도록 하세요. 이야기를 들려주세요. Java 업그레이드는 무료로 성능 향상을 제공합니다. 항상 벤치마크하세요.
featured image - 소프트웨어는 먼저 죽이지 않으면 성공한다: 성급한 최적화와 Java GC 이야기
Frankie HackerNoon profile picture
0-item

LinkedList가 더 빠를까요? 'for Each'를 'iterator'로 바꿔야 하나요? 이 `ArrayList`는 `Array`여야 합니까? 이 기사는 너무 악의적이어서 내 기억에 영구적으로 각인된 최적화에 대한 응답으로 작성되었습니다.


Java에 대해 직접 알아보고 가비지 수집기나 컨텍스트 전환에서 간섭을 해결하는 방법을 알아보기 전에 먼저 미래를 위한 코드 작성의 기본 사항을 살펴보겠습니다.


성급한 최적화는 모든 악의 근원입니다.


당신은 전에 그것을 들어 본 적이 있습니다. 성급한 최적화는 모든 악의 근원입니다. 글쎄요, 가끔은요. 소프트웨어를 작성할 때 나는 다음과 같은 점을 굳게 믿습니다.


  1. 최대한 설명적으로 ; 마치 이야기를 쓰는 것처럼 의도를 서술하도록 노력해야 합니다.


  2. 최대한 최적의 상태로 ; 즉, 언어의 기본을 알고 그에 맞게 적용해야 한다는 뜻입니다.

가능한 한 설명적으로

코드는 의도를 말해야 하며, 그 중 많은 부분이 메서드 및 변수의 이름을 지정하는 방식과 관련됩니다.


 int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great

변수 이름만으로도 이미 기능을 유추할 수 있습니다.


numItems 추상적이지만 backPackItems 예상되는 동작에 대해 많은 정보를 제공합니다.


또는 다음 방법이 있다고 가정해 보세요.


 List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; }

코드에 관한 한 이것은 다소 괜찮아 보입니다.


우리가 더 잘할 수 있을까요? 우리는 확실히 할 수 있습니다!


 List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; }

Collections.emptyList() 읽는 것은 new ArrayList<>(0);


위 코드를 처음 읽고 사용자가 실제로 국가를 방문한 적이 있는지 확인하는 보호 조항을 우연히 발견했다고 상상해 보세요. 또한 이것이 긴 클래스에 묻혀 있다고 상상해보십시오. Collections.emptyList() 를 읽는 것이 new ArrayList<>(0) 보다 더 설명적입니다. 또한 클라이언트 코드가 이를 수정할 수 없도록 불변성을 보장합니다.

가능한 한 최적

귀하의 언어를 알고 그에 맞게 사용하십시오. double 필요한 경우 Double 객체로 래핑할 필요가 없습니다. 실제로 필요한 것이 Array 뿐이라면 List 사용할 때도 마찬가지입니다.


스레드 간에 상태를 공유하는 경우 StringBuilder 또는 StringBuffer 사용하여 문자열을 연결해야 한다는 점을 알아두세요.


 // don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); }


데이터베이스를 인덱싱하는 방법을 알아보세요. 병목 현상을 예측하고 이에 따라 캐시합니다. 위의 모든 내용은 최적화입니다. 이는 최초의 시민으로서 인식하고 구현해야 하는 일종의 최적화입니다.

먼저 죽이는 방법은 무엇입니까?

저는 몇 년 전에 읽은 꿀팁을 결코 잊지 못할 것입니다. 사실, 저자는 빠르게 되돌아갔지만, 이는 선한 의도에서 얼마나 많은 악이 촉발될 수 있는지를 보여줍니다.


 // do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } }

지옥에서 온 가비지 수집기 해킹!


위의 코드가 작동하는 이유와 방법에 대한 자세한 내용은 원본 기사에서 확인할 수 있으며, 익스플로잇은 확실히 흥미롭지만 절대 수행해서는 안 되는 작업 중 하나입니다.


  • 부작용으로 작동합니다. Thread.sleep(0) 이 블록에서 목적이 없습니다.
  • 다운스트림 코드의 결함을 이용하여 작동합니다.
  • 이 코드를 물려받은 누구에게나 이는 모호하고 마법적인 일입니다.


언어가 제공하는 모든 기본 최적화를 사용하여 작성한 후 병목 현상이 발생한 경우에만 좀 더 복잡한 작업을 시작하십시오. 그러나 위와 같이 혼합물을 피하십시오.


Microsoft Copilot이 "상상한" Java의 미래 가비지 수집기에 대한 해석


가비지 수집기 처리하는 방법은 무엇입니까?

모든 작업이 완료된 후에도 Garbage Collector가 여전히 저항을 제공하는 경우 다음을 시도해 볼 수 있습니다.


  • 서비스가 지연 시간에 너무 민감하여 GC를 허용할 수 없는 경우 "Epsilon GC"를 사용하여 실행하고 GC를 완전히 피하세요 .
    -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC


    이는 OOM 예외가 발생할 때까지 분명히 메모리를 증가시키므로 수명이 짧은 시나리오이거나 프로그램이 개체를 생성하지 않도록 최적화되어 있습니다.


  • 서비스가 지연 시간에 다소 민감하지만 허용된 허용 범위에서 약간의 여유가 허용되는 경우 GC1을 실행하고 -XX:MaxGCPauseTimeMillis=100 (기본값은 250ms)과 같이 입력하세요.

  • 문제가 외부 라이브러리에서 발생하는 경우 그 중 하나가 세계 최고의 가비지 수집기인 System.gc() 또는 Runtime.getRuntime().gc() 를 호출한다고 가정하면 -XX:+DisableExplicitGC 실행하여 문제가 되는 동작을 재정의할 수 있습니다. -XX:+DisableExplicitGC



버전 시작

버전 종료

기본 GC

자바 1

자바 4

직렬 가비지 수집기

자바 5

자바 8

병렬 가비지 수집기

자바 9

전진

G1 가비지 컬렉터


참고 1: Java 15부터 ZGC 프로덕션 준비가 되어 있지만 -XX:+UseZGC 사용하여 명시적으로 활성화해야 합니다.


참고 2: VM이 2개 이상의 프로세서와 1792MB 이상의 힙 크기를 감지하면 VM은 시스템을 서버 클래스로 간주합니다. 서버 클래스가 아닌 경우 기본값은 Serial GC입니다 .


본질적으로, 애플리케이션의 성능 제약이 가비지 수집 동작과 직접적으로 연관되어 있다는 것이 확실하고 정보에 입각한 조정을 수행하는 데 필요한 전문 지식이 있는 경우 GC 튜닝을 선택하십시오. 그렇지 않으면 JVM의 기본 설정을 신뢰하고 애플리케이션 수준 코드 최적화에 집중하세요.

u/shiphe - 전체 댓글을 읽고 싶을 것입니다


탐색하고 싶은 기타 관련 라이브러리:

JMH(Java 마이크로벤치마크 하네스)

실제 벤치마킹 없이 느낌만으로 최적화 한다면 자신에게 해를 끼치는 것입니다. JMH는 알고리즘 성능을 테스트하는 사실상의 Java 라이브러리입니다. 그걸 써.

Java-스레드 친화성

프로세스를 특정 코어에 고정하면 캐시 적중률이 향상될 수 있습니다. 이는 기본 하드웨어와 루틴이 데이터를 처리하는 방식에 따라 달라집니다. 그럼에도 불구하고 이 라이브러리를 사용하면 구현이 매우 쉽기 때문에 CPU 집약적인 방법이 부담스럽다면 테스트해보고 싶을 것입니다.

LMAX 디스럽터

꼭 필요하지 않더라도 공부하고 싶어지는 도서관 중 하나입니다. 아이디어는 대기 시간이 매우 짧은 동시성을 허용하는 것입니다. 그러나 기계적 공감 에서부터 링 버퍼 에 이르기까지 구현 방식은 많은 새로운 개념을 가져옵니다. 나는 7년 전 그것을 처음 발견했을 때 그것을 소화하기 위해 밤새도록 노력했던 것을 아직도 기억합니다.

넷플릭스 jvmquake

jvmquake 의 전제는 JVM에서 문제가 발생하면 JVM이 중단되지 않고 종료되기를 원한다는 것입니다. 몇 년 전, 저는 메모리 제약이 심한 HTCondor 클러스터에서 시뮬레이션을 실행하고 있었는데, 때로는 "메모리 부족" 오류로 인해 작업이 중단되기도 했습니다.


이 라이브러리는 JVM을 강제 종료하여 실제 오류를 처리할 수 있도록 합니다. 이 특정한 경우 HTCondor는 작업 일정을 자동으로 다시 조정합니다.

마지막 생각들

내가 이 글을 쓰게 만든 코드는? 나는 훨씬 더 나쁜 것을 썼다. 난 여전히한다. 우리가 바랄 수 있는 최선은 계속해서 덜 어수선하게 만드는 것입니다.


나는 몇 년 후에 내 자신의 코드를 보면서 불만을 느낄 것으로 예상합니다.


그리고 그것은 좋은 징조입니다.



수정사항 및 감사합니다:


wasteofserver.com 에도 게시됨