이틀 전, 그룹의 친구가 인터뷰 중에 성능 최적화 방법에 대해 질문을 받았습니다. 이 글에서는 주로 이론적 분석에 중점을 두고 Java 성능 최적화를 위해 따라야 할 규칙을 전반적으로 살펴보겠습니다.
이 기사는 이론에 중점을 두고 있으며, 실습에서는 더 많은 사례를 사용하여 이 기사의 지식 포인트를 다듬으며 이는 반복적인 사고와 귀납에 적합합니다.
성능 최적화는 최적화 카테고리에 따라 비즈니스 최적화와 기술 최적화로 구분됩니다. 비즈니스 최적화의 효과도 매우 크지만 이는 제품 및 관리의 범주에 속합니다. 프로그래머로서 일상 업무에서 우리가 직면하는 최적화 방법은 주로 확립된 최적화 목표를 달성하기 위한 일련의 기술적 수단을 통해서입니다. 이 일련의 기술적 수단을 대략적으로 다음 7가지 범주로 요약할 수 있습니다.
보시다시피 최적화 방법은 컴퓨팅 리소스와 스토리지 리소스의 계획에 중점을 둡니다. 최적화 방법에는 공간과 시간을 교환하는 방법이 많이 있지만, 복잡성과 공간 문제를 고려하지 않고 계산 속도만 고려하는 것은 바람직하지 않습니다. 우리가 해야 할 일은 성능을 관리하면서 최적의 자원 활용을 달성하는 것입니다.
다음으로 최적화 방향 7가지를 간략하게 소개하겠습니다. 지루하다고 느껴지더라도 괜찮습니다. 이 글의 목적은 총점에 대한 개념과 이론적 기초에 대한 전반적인 이해를 제공하는 것입니다.
코드를 작성할 때 추출하여 공개 메소드로 만들 수 있는 반복 코드가 많다는 것을 알게 됩니다. 이렇게 하면 다음에 사용할 때 다시 작성할 필요가 없습니다.
이 아이디어는 재사용입니다. 위의 설명은 코딩 논리를 최적화한 것입니다. 데이터 액세스에도 동일한 재사용 상황이 있습니다. 생활에서든 코딩에서든 반복되는 일이 계속 발생하기 때문에 재사용하지 않으면 일과 생활이 더욱 피곤해질 것입니다.
소프트웨어 시스템에서 데이터 재사용에 관해 우리가 가장 먼저 생각하는 것은 버퍼링과 캐싱입니다. 이 두 단어의 의미는 완전히 다릅니다. 많은 학생들이 쉽게 혼동합니다.
버퍼는 일반적으로 데이터를 임시로 저장한 다음 일괄적으로 전송하거나 쓰는 데 사용됩니다. 순차적 방법은 주로 서로 다른 장치 간의 빈번하고 느린 무작위 쓰기를 완화하는 데 사용됩니다. 버퍼링은 주로 쓰기 작업을 대상으로 합니다.
캐시(Cache)는 일반적으로 읽은 데이터를 재사용하는 데 사용됩니다. 비교적 빠른 영역에서 캐시하여 주로 읽기 작업을 대상으로 합니다.
마찬가지로 Java에서 매우 자주 사용되는 데이터베이스 연결 풀, 스레드 풀 등 개체의 풀링 작업입니다. 이러한 객체의 생성 및 파괴 비용이 상대적으로 높기 때문에 다음에 사용할 때 이러한 객체를 임시로 저장하므로 시간이 많이 걸리는 초기화 작업을 다시 거칠 필요가 없습니다.
요즘 CPU는 매우 빠르게 발전하고 있으며 대부분의 하드웨어는 멀티 코어입니다. 특정 작업의 실행 속도를 높이려면 가장 빠르고 최상의 솔루션은 병렬로 실행되도록 하는 것입니다. 병렬 실행에는 세 가지 모드가 있습니다.
첫 번째 모드는 로드 밸런싱을 사용하여 트래픽이나 대규모 계산을 여러 부분으로 분할하고 동시에 처리하는 다중 시스템입니다. 예를 들어 Hadoop은 MapReduce를 사용하여 작업을 분할하고 동시에 여러 시스템에서 계산을 수행합니다.
두 번째 모드는 멀티 프로세스를 사용하는 것입니다. 예를 들어 Nginx는 NIO 프로그래밍 모델을 사용하여 작업자 프로세스를 통합된 방식으로 관리한 다음 작업자 프로세스가 실제 요청 프록시를 수행합니다. 이는 하드웨어의 여러 CPU를 효과적으로 활용할 수도 있습니다.
세 번째 모드는 Java 프로그래머가 가장 많이 접하는 멀티스레딩을 사용하는 모드입니다. 예를 들어 Netty는 Reactor 프로그래밍 모델을 사용하고 NIO도 사용하지만 스레드 기반입니다. Boss 스레드는 요청을 수신한 다음 실제 비즈니스 계산을 위해 해당 Worker 스레드에 요청을 예약하는 데 사용됩니다.
Golang과 같은 언어는 더 가벼운 코루틴을 가지고 있습니다. 코루틴은 스레드보다 더 가벼운 존재이지만 Java에서는 아직 성숙하지 않았기 때문에 많이 소개하지는 않겠습니다. 그러나 본질적으로 멀티용이기도 합니다. -핵심 애플리케이션으로 작업을 병렬로 실행할 수 있습니다.
컴퓨팅의 또 다른 최적화는 동기에서 비동기로 변경하는 것입니다. 여기에는 일반적으로 프로그래밍 모델의 변경이 포함됩니다. 동기 모드에서는 성공 또는 실패 결과가 반환될 때까지 요청이 차단됩니다. 프로그래밍 모델은 간단하지만 일정 기간 동안 갑작스럽고 편향된 트래픽을 처리할 때 특히 문제가 되며 요청이 쉽게 실패할 수 있습니다.
비동기 작업은 쉽게 수평 확장을 지원하고 순간적인 압박을 완화하며 요청을 원활하게 할 수 있습니다. 동기식 요청은 주먹으로 철판을 치는 것과 같습니다. 비동기식 요청은 주먹으로 스펀지를 치는 것과 같습니다. 이 프로세스를 상상할 수 있습니다. 후자는 확실히 유연하고 경험이 더 사용자 친화적입니다.
마지막은 비즈니스를 최적화하고 경험을 개선하기 위해 싱글톤 모드, 프록시 모드 등과 같은 몇 가지 일반적인 디자인 패턴을 사용하는 것입니다. 예를 들어, 스윙 창을 그릴 때 더 많은 그림을 표시하려면 자리 표시자를 먼저 로드한 다음 백그라운드 스레드를 통해 필요한 리소스를 천천히 로드하면 창이 멈추는 것을 방지할 수 있습니다.
다음으로 결과 집합의 최적화에 대해 소개하겠습니다. 좀 더 직관적인 예를 들자면, 우리 모두는 XML의 표현이 매우 좋다는 것을 알고 있는데 왜 아직도 JSON이 존재하는 걸까요? 작성이 간편해진 것 외에도 크기가 작아지고 전송 효율성과 파싱 효율성이 높아진 것이 중요한 이유입니다. Google의 Protobuf와 마찬가지로 크기도 더 작아졌습니다. 가독성은 떨어지지만 일부 높은 동시성 시나리오(예: RPC)에서는 효율성이 크게 향상될 수 있습니다. 이는 결과 집합의 일반적인 최적화입니다.
현재 웹 서비스가 모두 C/S 모드이기 때문입니다. 데이터가 서버에서 클라이언트로 전송되면 여러 개의 복사본을 분산시켜야 합니다. 데이터의 양은 소량의 스토리지가 줄어들 때마다 전송 성능과 비용이 크게 향상됩니다.
Nginx와 마찬가지로 GZIP 압축은 일반적으로 전송된 콘텐츠를 컴팩트하게 유지하기 위해 켜져 있습니다. 클라이언트에는 압축 해제를 용이하게 하기 위해 소량의 컴퓨팅 성능만 필요합니다. 이 작업은 분산되어 있으므로 성능 저하가 고정되어 있습니다.
이 원칙을 이해하면 결과 세트 최적화에 대한 일반적인 아이디어를 볼 수 있습니다. 반환된 데이터를 최대한 단순하게 유지해야 합니다. 클라이언트에 필요하지 않은 일부 필드가 있는 경우 해당 필드를 코드에서 제거하거나 SQL 쿼리에서 직접 제거하십시오.
적시성에 대한 요구 사항은 높지 않지만 처리 능력에 대한 요구 사항은 높은 일부 비즈니스에 적합합니다. 버퍼의 경험을 통해 배우고 네트워크 연결 상호 작용을 최소화하며 일괄 처리를 사용하여 처리 속도를 높여야 합니다.
결과셋은 두 번 사용될 가능성이 높으며, 캐시에 추가해도 되지만 여전히 속도가 부족합니다. 이때 데이터 액세스 속도를 높이기 위해 인덱스나 비트맵 비트맵을 사용하여 데이터 수집 처리를 최적화해야 합니다.
일반적인 개발에서는 많은 공유 리소스가 필요합니다. 이러한 공유 리소스 중 일부는 HashMap과 같은 독립형이고, 일부는 데이터베이스 행과 같은 외부 저장소이며, 일부는 특정 Redis 키의 Setnx와 같은 단일 리소스입니다. , 분산 거래 등
실제로는 잠금과 관련된 성능 문제가 많습니다. 우리 대부분은 데이터베이스 행 잠금, 테이블 잠금, Java의 다양한 잠금 등을 생각합니다. CPU 명령 수준 잠금, JVM 명령 수준 잠금, 운영 체제 내부 잠금 등과 같은 하위 수준에서는 어디에나 있다고 말할 수 있습니다.
동시성만 리소스 충돌을 일으킬 수 있습니다. 즉, 동시에 하나의 처리 요청만이 공유 자원을 얻을 수 있습니다. 리소스 충돌을 해결하는 방법은 잠그는 것입니다. 또 다른 예는 본질적으로 일종의 잠금인 트랜잭션입니다.
잠금 수준에 따라 잠금은 낙관적 잠금과 비관적 잠금으로 나눌 수 있습니다. 잠금 유형에 따라 낙관적 잠금은 확실히 더 효율적이며 작업 예약 측면에서 잠금은 일부 있습니다. 미묘한 차이.
리소스에 대한 콘텐츠는 심각한 성능 문제를 일으킬 것이므로 잠금 없는 대기열에 대한 연구가 있을 것이며 성능 향상은 엄청날 것입니다.
알고리즘은 복잡한 비즈니스의 성능을 크게 향상시킬 수 있지만 실제 비즈니스에서는 변형되는 경우가 많습니다. 스토리지가 점점 더 저렴해짐에 따라 CPU가 매우 부족한 일부 기업에서는 처리 속도를 높이기 위해 공간을 시간과 교환하는 경우가 많습니다.
알고리즘은 코드 튜닝에 속합니다. 코드 튜닝에는 많은 코딩 기술이 필요하며 사용자는 사용되는 언어의 API에 매우 익숙해야 합니다. 때로는 알고리즘과 데이터 구조를 유연하게 사용하는 것도 코드 최적화의 중요한 부분입니다. 예를 들어, 시간 복잡성을 줄이기 위해 일반적으로 사용되는 방법에는 재귀, 이등분, 정렬, 동적 프로그래밍 등이 포함됩니다.
훌륭한 구현은 잘못된 구현보다 시스템에 더 큰 영향을 미칩니다. 예를 들어, List 구현인 LinkedList와 ArrayList는 무작위 액세스 성능이 여러 차수 다르기 때문에 CopyOnWriteList는 쓰기 중 복사 방법을 사용하므로 읽기 작업이 많고 읽기 작업이 적은 시나리오에서 잠금 충돌을 크게 줄일 수 있습니다. 글쓰기. 동기화를 사용해야 하는 경우와 스레드 안전을 사용해야 하는 경우에는 코딩 기능에 대한 요구 사항도 더 높습니다.
이 부분의 지식은 일상 업무에서 지식을 축적하는 데 주의를 기울여야 합니다. 다음 수업에서는 더 중요한 지식 포인트를 선택하고 설명합니다.
일상 프로그래밍에서는 좋은 디자인 컨셉과 뛰어난 성능을 갖춘 일부 구성요소를 사용해 보세요. 예를 들어 Netty를 사용하면 더 이상 이전 Mina 구성 요소를 선택할 필요가 없습니다. 성능 요소를 고려하여 시스템을 설계할 때 SOAP와 같이 시간이 많이 걸리는 프로토콜을 선택하지 마십시오. 또 다른 예로, 좋은 구문 분석기(예: JavaCC 사용)는 정규식보다 훨씬 더 효율적입니다.
즉, 테스트 분석을 통해 시스템의 병목 현상이 발견되면 핵심 구성 요소를 보다 효율적인 구성 요소로 교체해야 합니다. 이 경우 어댑터 패턴이 매우 중요합니다. 이것이 바로 많은 회사가 기존 구성 요소 위에 추상화 계층을 추가하는 것을 선호하는 이유이며, 기본 구성 요소가 전환될 때 상위 계층 애플리케이션은 이를 인식하지 못합니다.
Java는 JVM 가상 머신에서 실행되기 때문에 많은 기능이 JVM에 의해 제한됩니다. JVM 가상 머신을 최적화하면 JAVA 프로그램의 성능도 어느 정도 향상될 수 있습니다. 매개변수를 잘못 구성하면 OOM과 같은 심각한 결과를 초래할 수도 있습니다.
현재 널리 사용되는 가비지 컬렉터는 G1입니다. 매우 적은 수의 매개변수 구성으로 메모리를 효율적으로 재활용할 수 있습니다. CMS 가비지 수집기는 Java 14에서 제거되었습니다. GC 시간을 제어할 수 없으므로 가능하면 사용을 피해야 합니다.
JVM 성능 튜닝에는 모든 측면에서 트레이드오프가 수반되며 이는 전신에 영향을 미치는 경우가 많으며 모든 측면의 영향을 종합적으로 고려해야 합니다. 따라서 JVM 내의 일부 작동 원리를 이해하는 것이 특히 중요합니다. 이는 코드를 더 깊이 이해하고 보다 효율적인 코드를 작성하는 데 도움이 됩니다.
위는 코드 최적화의 7가지 일반적인 방향에 대한 간략한 소개를 통해 모든 분들께 성능 최적화 내용에 대한 전반적인 이해를 제공하겠습니다. 이 7가지 주요 방향은 코드 최적화의 가장 중요한 방향입니다. 물론 성능 최적화에는 데이터베이스 최적화, 운영 체제 최적화, 아키텍처 최적화 및 기타 내용도 포함됩니다. 요약.
위 내용은 인터뷰: Java 성능 최적화에 어떤 방법을 사용할 수 있는지 알고 계십니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!