효율적인 병렬 잠금 최적화
4163 단어 Java 가상 시스템
핫스팟 가상 기기는 적응성 스핀닝(Adaptive Spinning), 잠금 제거(Lock Elimination), 잠금 조화(Lock Coarsening), 경량급 잠금(Lightweight Locking)과 편향 잠금(Biased Locking) 등 다양한 잠금 최적화 기술을 구현했는데, 이들 기술은 스레드 간 데이터를 보다 효율적으로 공유해 경쟁 문제를 해결하고 프로그램의 실행 효율을 높이기 위한 것이다.
자전거 자물쇠와 자동 적응 자전거
앞에서 라인 간의 상호 배척 동기화를 토론할 때 상호 배척 동기화가 성능에 가장 큰 영향을 미치는 것은 막힘의 실현이라고 언급했다. 라인을 끊고 복구하는 작업은 모두 내부 핵 상태로 들어가 완성해야 한다. 이런 조작은 시스템의 병행 성능에 큰 압력을 가져왔다.공유 데이터의 잠금 상태는 단지 짧은 시간 동안 지속될 뿐이며, 이 시간을 위해 라인을 끊고 복구하는 것은 가치가 없다.만약 물리적 기계가 1개 이상의 프로세서를 가지고 두 개 이상의 라인을 동시에 실행할 수 있다면, 우리는 뒤에서 자물쇠를 요청하는 라인을 '잠깐만 기다려' 라고 할 수 있지만, 프로세서의 실행 시간을 포기하지 않고 자물쇠를 가진 라인이 곧 자물쇠를 풀 수 있는지 볼 수 있다.라인을 기다리게 하기 위해서, 우리는 라인이 바쁜 순환 (자전거) 을 실행하도록 하기만 하면, 이 기술은 이른바 자전거 자물쇠이다.자전거 대기는 막힌 것을 대체할 수 없다. 자전거 자체가 전환 라인의 비용을 피하지만 프로세서 시간을 차지해야 하기 때문에 자물쇠가 차지하는 시간이 짧으면 자전거 대기의 효과가 매우 좋다. 반대로 자물쇠가 차지하는 시간이 길면 자전거 라인은 프로세서 자원을 헛되이 소모하고 아무런 유용한 작업도 하지 않으며 오히려 성능상의 낭비를 가져올 수 있다.따라서 자전거가 기다리는 시간은 반드시 일정한 한도가 있어야 한다. 만약에 자전거가 제한 횟수를 초과하고도 자물쇠를 얻는 데 성공하지 못하면 전통적인 방식으로 라인을 걸어야 한다.자선 횟수는 기본적으로 10회이며 -XX:PreBlockSpin을 통해 변경할 수 있습니다.JDK 1.6에 자체 적응형 자전거 자물쇠를 도입했다.적응은 자전거의 시간이 고정되지 않고 이전에 같은 자물쇠에 잠겼던 자전거의 시간과 자물쇠의 소유자의 상태에 의해 결정된다는 것을 의미한다.만약에 같은 자물쇠 대상에서 자전거 대기가 자물쇠를 얻는 데 성공했고 자물쇠를 가진 라인이 실행 중이라면 가상 기기는 이번 자전거도 다시 성공할 가능성이 높다고 생각하고 자전거 대기가 상대적으로 긴 시간, 예를 들어 100개의 순환을 지속할 수 있도록 허용할 것이다.또한 어떤 자물쇠에 대해 자전거가 성공적으로 획득되지 않았다면, 나중에 이 자물쇠를 얻으려면 프로세서 자원을 낭비하지 않도록 자전거 과정을 생략할 수 있습니다.스스로 자전거에 적응하고 프로그램의 운행과 성능 모니터링 정보가 끊임없이 보완됨에 따라 가상 기기가 프로그램 잠금의 상황에 대한 예측이 점점 정확해지고 가상 기기는 점점 똑똑해질 것이다.
잠금 해제
자물쇠 제거란 가상 기기의 실시간 컴파일러가 운행할 때 일부 코드에 대해 동기화를 요구하지만 공유 데이터 경쟁이 존재하지 않는 자물쇠가 검출되어 제거되는 것을 말한다.자물쇠 제거의 주요 판정 근거는 탈출 분석의 데이터 지원에서 비롯된다. 만약에 하나의 코드에 있다고 판단되면 쌓인 모든 데이터가 탈출하지 않고 다른 라인에 접근할 수 없다. 그러면 그것들을 창고 데이터로 취급할 수 있다. 그들은 라인이 개인적인 것이고 동기화 자물쇠는 자연히 진행할 필요가 없다.예를 들어 다음 코드:
public String concatString(String s1, String s2, String s3) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3);
return sb.toString();
}
위 코드에서 각 StringBuffer.append () 방법에는 모두 동기화 블록이 있는데 자물쇠가 sb 대상이다.가상 기기가 변수 sb를 관찰한 결과 그의 동적 역할 영역은 concatstring () 방법 내부에 제한되어 있음을 발견했다. 즉, sb의 모든 인용은 concatstring () 방법 이외에 영원히 도망가지 않고 다른 라인은 접근할 수 없기 때문에 여기에 자물쇠가 있지만 안전하게 제거될 수 있다. 즉각 컴파일한 후에 이 코드는 모든 동기화 블록을 무시하고 직접 실행된다.
자물쇠 조화
원칙적으로 우리는 코드를 작성할 때 동기화 블록의 작용 범위를 최대한 작게 제한하는 것을 추천한다. 공유 데이터의 실제 작용 영역에서만 동기화를 하는 것이다. 이렇게 하면 동기화가 필요한 조작 수량을 최대한 작게 하기 위해서이다. 만약에 자물쇠 경쟁이 존재한다면 자물쇠를 기다리는 라인도 가능한 한 빨리 자물쇠를 받을 수 있다.대부분의 경우 위의 원칙은 모두 정확하지만 일련의 연속 조작이 한 대상에게 반복적으로 자물쇠를 잠그고 잠금을 해제한다. 심지어 자물쇠를 잠그는 조작이 순환체에 나타나면 라인 경쟁이 없어도 서로 밀어붙이고 동기화를 빈번하게 하면 불필요한 성능 손실을 초래할 수 있다.StringBuffer의 append () 방법은 이런 상황에 속한다.만약 가상 기기가 이러한 자질구레한 조작이 같은 대상에 자물쇠를 채우는 것을 탐지하면 자물쇠가 동기화되는 범위를 전체 조작 서열의 외부로 확장(조화)할 것이다. 위의 코드를 예로 들면 첫 번째 append() 조작 전까지 마지막 append() 조작 후까지 확장한다. 이렇게 하면 자물쇠를 한 번만 추가하면 된다.
경량급 자물쇠
경량급 자물쇠는 운영체제의 상호 배척량을 사용하여 실현되는 전통적인 자물쇠(중량급 자물쇠)에 비해 경량급 자물쇠는 중량급 자물쇠를 대체하는 것이 아니다. 그의 본뜻은 다선정 경쟁이 없는 전제에서 전통적인 중량급 자물쇠가 운영체제의 상호 배척량을 사용하여 발생하는 성능 손실을 줄이는 것이다.경량급 자물쇠의 잠금 해제 과정은 모두 CAS 조작을 통해 실현된다.만약 두 개 이상의 라인이 같은 자물쇠를 다투어 사용한다면 경량급 자물쇠는 더 이상 효과가 없고 중량급 자물쇠로 팽창해야 한다.경량급 자물쇠가 프로그램의 동기화 성능을 향상시킬 수 있는 근거는'대부분의 자물쇠에 대해 전체 동기화 주기 내에 경쟁이 존재하지 않는다'는 것이다. 이것은 경험 데이터이다.만약 경쟁이 없다면 경량급 자물쇠는 CAS 조작을 사용하여 상호 배척량을 사용하는 비용을 피할 수 있지만 자물쇠 경쟁이 존재하면 상호 배척량의 비용 외에 추가로 CAS 조작이 발생하기 때문에 경쟁이 있는 상황에서 경량급 자물쇠는 전통적인 중량급 자물쇠보다 더 느릴 것이다.
편향 자물쇠
편향 잠금도 JDK 1.6에 도입된 잠금 최적화로 데이터가 경쟁 상황에서 동기화되는 원어를 없애고 프로그램의 운행 성능을 향상시키는 데 목적을 둔다.경량급 자물쇠가 경쟁 없이 CAS 조작을 사용하여 동시 사용의 상호 배척량을 제거한다면 편향 자물쇠는 경쟁 없이 전체 동기화를 제거하고 CAS 조작도 하지 않는 것이다.편향 자물쇠의'편향'은 이 자물쇠가 첫 번째로 그의 라인을 얻는 데 편향된다는 뜻이다. 만약 다음 실행 과정에서 이 자물쇠가 다른 라인에 의해 획득되지 않는다면 편향 자물쇠를 가진 라인은 영원히 동기화될 필요가 없다.다른 라인이 이 자물쇠를 가져오려고 시도할 때, 편향 모드가 끝납니다.잠금 대상이 현재 잠긴 상태에 있는지에 따라 편향을 취소한 후 잠기지 않거나 경량급 잠금 꼭대기 상태로 회복하면 후속 동기화 작업은 경량급 잠금처럼 실행된다.편향 잠금은 동기화되지만 경쟁이 없는 프로그램 성능을 높일 수 있다.그 역시 효익 균형적 성격을 가진 최적화이다. 즉, 그가 반드시 프로그램 운행에 유리한 것은 아니다. 만약에 프로그램의 대다수 자물쇠가 항상 여러 개의 다른 라인에 방문된다면 편향 모델은 불필요한 것이다.때때로 파라미터-XX:-UseBiasedLocking을 사용하여 편향 잠금 최적화를 금지하면 오히려 성능을 향상시킬 수 있다.