2016-10-21 3 views
0

내가 아는 한 최종은 변수/개체를 캐시 할 수 있다는 것을 CPU에 알려주는 것입니다 (어쨌든 CPU와 CPU는 다릅니다.) x86 CPU는 실제로 코어 캐시 L1을 수행하고 있다고 생각합니다. 내 질문 다음 예에서는 myObjectFinal 개체를 final로 설정하여 CPU가 캐시 할 수있게 한 다음 내부 값을 변경합니다. myObject가 변경되는 것이 보장되지 않는다는 것을 의미합니까?최종 및 메모리 누수

final을 설정하고 CPU가 캐시를 결정하면 참조가 손상됩니까?

myObject를 변경하면 캐시 된 최종본도 변경 될 수 있습니다.

// thread 1 
volatile MyObject myObject = new MyObject(); 
// thread 2 
final MyObject myObjectFinal = myObject; 
myObjectFinal.setData(1); 
// thread 1 
myObject.setData(2); 

코드가 정확히 무슨 뜻인지 알 수 없습니다. 따라서 주요 질문은 가변적 인 객체를 최종적으로 또는 휘발성으로 만드는 것이 객체가 CPU에 의해 캐시되는 방식에 어떤 영향을 미치는지에 있습니다. 그렇다면 변경 가능한 객체에서 final 또는 volatile을 사용해야 할 때?

CPU가 변경 ​​가능한 객체를 캐시하거나 final/volatile이 아무런 영향을 미치지 않고 코딩 일관성을 위해서만 사용됩니까?

+0

귀하의 질문은 '최종'에 대한 것보다 '휘발성'키워드에 관한 것입니다. 당신의 (멤버) 변수가'volatile'으로 선언되면, JVM은 다른 스레드의 변경 사항이 현재 스레드에서 볼 수 있음을 확인합니다. '휘발성'키워드가 없으면'final' 키워드를 사용하지 않아도됩니다. –

+1

** 객체 myObjectFinal이 final로 설정되어 있습니다 ** - 아니요,'Object'가'final'으로 설정되어 있지 않습니다. ** Object Reference **가'final'으로 설정되어 있습니다. 일반적으로 '휘발성'및 '최종'은 원시 유형의 가시성에 대해 의미가 있으며 다른 시나리오에서는 명시적인 '동기화'가 필요합니다. –

+0

@Sabir 분명히 나는 ​​객체 참조에 final을 넣고있다. 내가 객체를 복사하거나 프리미티브를 사용하면 제 질문은 의미가 없습니다. 귀하의 의견은 전혀 도움이되지 않습니다. 그리고 다시 동기화는 휘발성과 다릅니다. 이 질문은 코드의 일부를 원자 적으로하지 않는 CPU에서의 캐싱에 관한 것입니다. – chris

답변

0

final은 초기화 후에 값을 변경할 수 없다는 것을 의미합니다.

최종 변수가 할당되면 항상 동일한 값이 포함됩니다. 최종 변수가 객체에 대한 참조를 보유하면 객체의 상태에 따라 객체의 상태가 변경 될 수 있지만 변수는 항상 동일한 객체를 참조합니다. https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4

그래서 당신은 일이 일어날 나쁜 myObjectFinal, 아무것도

여러 스레드에서 동일한 개체를 변경하는 경우에는, 당신은 동기화가 필요합니다 재 할당없는 경우를 제외하고. (volatile을 사용하지 마십시오)

우선, 객체 지향을 제거하십시오. 객체는 가상 구조입니다. 우리는 레지스터와 캐시와 같은 저수준의 것들에 대해 이야기하고 있습니다 - 프리미티브 만 있습니다 (모든 클래스는 프리미티브 세트로 축소 될 수 있습니다)

왜 우리는 캐시가 필요한지 간단합니다. 메모리가 너무 느립니다. 그래서 메모리 접근의 수를 최소한으로 줄이는 것이 목표입니다.

다음 코드 고려해 일반 컴퓨터의 내부

1. x=0; 
2. if (x == 1) 
3. x++ 

지금 (레지스터 최적화없이) 이런 일을 발생

1. write 0 into a register 
    write the register into the memory 
2. read x from the memory into a CPU-register 
    compare it with 1 and go to 3 or 4 
3. read x from the memory into a CPU-register 
    increment register 
    write the register into the memory 

중복 메모리 작업을 많이 ...하지만 그들을 제거 할 수있다

1. write 0 into a register 
    write the register into the memory 
2. compare the register with 1 and go to 3 or 4 
3. increment register 
    write the register into the memory 

우리는 regi에 x의 값을 유지한다. ster. 그렇다면 모든 변수를 레지스터에 보관하는 것이 어떻습니까? 우선 많은 레지스터를 사용할 수 없기 때문에 모든 것을 저장할 수는 없습니다. 레지스터에 변수를 보유하는 것이 일시적입니다.(예를 들어 스코프 내부) 다른 문제는 레지스터에서 다른 코어 및/또는 장치를 읽을 수 없다는 것입니다. 그런 다음 변수를 다시로드하거나 다시 작성해야합니다.

변수를 final로 선언하면 변경되지 않습니다. 변수는 레지스터에 저장 될 수 있으므로 불일치 문제는 없습니다. 그러나 이것은 메모리 내부에 저장되어 있기 때문에 다른 스레드가 변수에 액세스 할 수 없다는 것을 의미하지는 않습니다. 메모리의 값이 항상 유효하다는 것을 보장합니다. 그러나 최종 변수에 상관없이 모든 변수는 짧은 시간 동안 레지스터에 저장 될 수 있습니다. 다중 스레드/코어의 일관성을 보장하려는 경우 동기화 도구를 사용해야하는 이유가 레지스터에서 메모리로 강제로 되돌아갑니다.

그러나 실제 CPU 캐시는 완전히 다른 주제입니다. (당신이 정말로 어셈블리에서 미친 low-low 레벨 물건을하지 않는다면) 메모리 액세스 패턴에 의해서만 영향을받는 완벽한 투명 시스템 (하드웨어 측에서 실행!). 여기에 짧게하기 : CPU 캐시는 기본적으로 모든 바이트를 캐시합니다. 명시 적으로 수행하지 않는다면 말입니다.

+0

JLS를 이미 참조 했으므로 여기에 관련 참조가 있습니다. [JLS 17.5 final field semantics] (https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5)). 마지막 필드는 절대적으로 CPU 캐싱에 영향을 미치며 JVM에서 CPU 레지스터의 최종 필드 값을 캐시하기로 결정할 수도 있습니다. –

+0

예 레지스터,하지만 그는 명시 적으로 영향을 받아야 할 L1- 코어 캐시를 언급했습니다. – Domso

+0

CPU 레지스터 저는 메모리 누수를 고려할 때 L1보다 다른 스레드/코어에서 훨씬 더 접근하기 어렵다고 생각합니다. 질문은 정확하게 캐시 된 곳이 아닌 메모리 누수에 관한 것입니다. – chris