메모리 장벽
메모리 장벽
각 CPU에는 자체 캐시가 있습니다(일부는 L1, L2, L3도 있음). 캐시의 목적은 성능을 향상하고 매번 액세스해야 하는 것을 방지하는 것입니다. 내부적으로. 그러나 이에 대한 단점도 명백하다. 실시간으로 메모리와 정보를 교환할 수 없고, 서로 다른 CPU에서 실행되는 서로 다른 스레드는 동일한 변수에 대해 서로 다른 캐시 값을 갖는다.
휘발성 키워드로 변수를 수정하면 위의 문제를 해결할 수 있는데, 휘발성은 어떻게 이를 수행할까요? 이것이 메모리 장벽입니다. 메모리 장벽은 하드웨어 계층의 개념입니다. 하드웨어 플랫폼마다 메모리 장벽을 구현하는 방법이 다릅니다. Java는 이러한 차이점을 보호함으로써 JVM에 의한 메모리 장벽 생성 지침을 통합합니다.
하드웨어 계층에는 읽기 장벽과 쓰기 장벽인 로드 장벽과 저장 장벽이라는 두 가지 유형의 메모리 장벽이 있습니다.
메모리 장벽에는 두 가지 기능이 있습니다.
로드 장벽의 경우 명령 앞에 로드 장벽을 삽입하면 캐시의 데이터가 무효화되고 데이터가 주 메모리에서 다시 로드됩니다. /p>
Store Barrier의 경우 명령어 뒤에 Store Barrier를 삽입하면 쓰기 캐시의 최신 데이터를 업데이트하고 이를 주 메모리에 기록하여 다른 스레드에 표시할 수 있습니다.
Java에는 일반적으로 네 가지 소위 메모리 장벽이 있습니다. 즉 LoadLoad, StoreStore, LoadStore 및 StoreLoad는 실제로 위 두 가지의 조합으로 일련의 장벽 및 데이터 동기화 기능을 완성합니다.
LoadLoad 장벽: Load1과 같은 명령문의 경우 Load2에서 읽을 데이터와 후속 읽기 작업에 액세스하기 전에 Load1에서 읽을 데이터를 읽어야 합니다.
StoreStore 장벽: Store1, StoreStore, Store2와 같은 문의 경우 Store2 및 후속 쓰기 작업이 실행되기 전에 Store1의 쓰기 작업이 다른 프로세서에 표시됩니다.
LoadStore 장벽: Load1; LoadStore2와 같은 명령문의 경우 Store2 및 후속 쓰기 작업이 플러시되기 전에 Load1에서 읽을 데이터를 완전히 읽어야 합니다.
StoreLoad 장벽: Store1, Load2와 같은 문의 경우 Load2 및 모든 후속 읽기 작업이 실행되기 전에 Store1에 대한 쓰기가 모든 프로세서에 표시됩니다. 4개 장벽 중 오버헤드가 가장 크다. 대부분의 프로세서 구현에서 이 장벽은 다른 세 가지 메모리 장벽의 기능을 갖는 보편적인 장벽입니다.
? Volatile의 메모리 장벽 전략은 매우 엄격하고 보수적이며 매우 비관적이며 안전하지 않습니다. p> 메모리 배리어의 기능으로 인해 휘발성 변수 및 기타 명령어의 순서가 바뀌는 것을 방지하고 스레드 간의 통신이 이루어지므로 휘발성이 잠금 특성을 발휘하게 됩니다.
최종 필드의 경우 컴파일러와 CPU는 두 가지 정렬 규칙을 따릅니다.
즉, 위 규칙의 의미를 이러한 방식으로 이해할 수 있는지 확인해야 합니다. 객체의 모든 최종 필드는 나중에 참조하고 읽을 수만 있습니다. 이는 메모리 장벽의 역할이기도 합니다.
최종 필드 작성: 컴파일러가 최종 필드 작성을 마친 후 구조가 끝나기 전에 StoreStore 장벽이 삽입되어 이전 최종 필드가 쓰기는 다른 스레드의 영향을 받지 않으며 CPU에 표시되며 재정렬을 방지합니다.
최종 필드 읽기: 위의 규칙 2에서 2단계 작업을 재정렬할 수 없는 메커니즘은 최종 필드를 읽기 전에 LoadLoad 장벽이 삽입된다는 것입니다.
X86 프로세서에서는 CPU가 쓰기-쓰기 작업의 순서를 바꾸지 않기 때문에 StoreStore 장벽이 생략되고 X86은 논리적 종속성이 있는 작업의 순서를 바꾸지 않습니다. 따라서 LoadLoad도 생략됩니다.