Programing

JVM GC가 참조 비교 중에 객체를 이동하여 양쪽이 동일한 객체를 참조하더라도 비교가 실패 할 수 있습니까?

lottogame 2020. 12. 12. 09:57
반응형

JVM GC가 참조 비교 중에 객체를 이동하여 양쪽이 동일한 객체를 참조하더라도 비교가 실패 할 수 있습니까?


GC가 때때로 메모리에서 객체를 이동한다는 것은 잘 알려져 있습니다. 그리고 객체가 이동 될 때 (사용자 코드가 호출되기 전에) 모든 참조가 업데이트되는 한, 이것은 완벽하게 안전해야한다는 것을 이해합니다.

그러나 누군가가 참조 비교 중에 GC에 의해 객체가 이동되어 두 참조가 동일한 객체를 참조해야 할 때도 비교가 실패 할 수 있기 때문에 참조 비교가 안전하지 않을 수 있다고 언급하는 것을 보았습니다.

즉, 다음 코드가 "true"를 인쇄하지 않는 상황이 있습니까?

Foo foo = new Foo();
Foo bar = foo;
if(foo == bar) {
    System.out.println("true");
}

나는 이것을 인터넷 검색을 시도했고 신뢰할 수있는 결과가 부족하여 이것을 진술 한 사람이 잘못되었다고 믿게되었지만 그가 옳다는 것을 나타내는 것처럼 보이는 포럼 게시물 (이것 과 같은 )을 찾았습니다 . 그러나 그 스레드에는 또한 사람들이 그렇지 않다고 말하는 사람들이 있습니다.


Java Bytecode 명령어는 항상 GC와 관련하여 원자 적입니다 (즉, 단일 명령어가 실행되는 동안주기가 발생할 수 없음).

GC가 실행되는 유일한 시간은 두 바이트 코드 명령어 사이입니다.

코드의 if 명령어에 대해 javac가 생성하는 바이트 코드를 살펴보면 GC가 효과가 있는지 간단히 확인할 수 있습니다.

// a GC here wouldn't change anything
ALOAD 1
// a GC cycle here would update all references accordingly, even the one on the stack
ALOAD 2
// same here. A GC cycle will update all references to the object on the stack
IF_ACMPNE L3
// this is the comparison of the two references. no cycle can happen while this comparison
// "is running" so there won't be any problems with this either

추가로, GC가 바이트 코드 명령어 실행 중에 실행될 수 있었다고하더라도 객체의 참조는 변경되지 않습니다. 주기 전후에도 여전히 동일한 대상입니다.

간단히 말해서 귀하의 질문에 대한 대답은 '아니요'이며 항상 사실로 출력됩니다.


출처:

https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.21.3

짧은 대답은 Java 8 사양을 살펴보면 아니오입니다.

==작업자가 항상 오브젝트 동등 검사를 수행한다 (참조도가 null 주어진). 개체가 이동하더라도 개체는 여전히 동일한 개체 입니다.

그러한 효과가 보이면 방금 JVM 버그를 발견 한 것 입니다. 가서 제출하십시오.

물론, JVM의 일부 모호한 구현은 이상한 성능 이유가 무엇이든 이것을 강제하지 않을 수 있습니다. 그렇다면 해당 JVM에서 간단히 이동하는 것이 현명 할 것입니다.


TL; DR

그런 식으로 생각해서는 안됩니다. 어두운 곳입니다. Java는 사양을 명확하게 명시했으며 의심해서는 안됩니다.

2.7. 개체의 표현

Java Virtual Machine은 객체에 대한 특정 내부 구조를 요구하지 않습니다.

출처 : JVMS SE8.

나는 그것을 의심한다! 이 매우 기본적인 운영자를 의심 할 수 있다면 다른 모든 것을 의심 할 수도 있습니다. 신뢰 문제로 좌절하고 편집증에 빠지는 것은 여러분이 원하는 곳이 아닙니다.

나에게 일어난다면? 그러한 버그는 존재해서는 안됩니다. 몇 년 전에 발생한 버그를보고 한 Oracle 토론과 어떻게 든 토론 OP는 이러한 버그에 대한 신뢰할 수있는 문서 없이도 며칠 동안 존재하지 않는 이유없이이를 팝업하기로 결정했습니다. 그러나 그러한 버그 또는 기타 문제가 발생한 경우 여기에서 제출 하십시오 .

걱정을 없애기 위해 Java는 포인터 접근 방식을 JVM 포인터 테이블 로 조정 했습니다 . 여기 에서 효율성에 대해 자세히 읽을 수 있습니다 .


GC는 상태가 잘 정의되어 있고 JVM이 모든 항목이 레지스터 / 스택 / 힙에있는 정확한 지식을 가지고있는 프로그램의 지점에서만 발생하므로 개체가 이동 될 때 모든 참조를 수정할 수 있습니다.

즉, 임의의 어셈블리 명령 실행 사이에는 발생할 수 없습니다. 개념적으로는 이전 지침에 의해 생성 된 모든 참조를 조정하는 GC가있는 JVM의 바이트 코드 명령 사이에서 발생한다고 생각할 수 있습니다.


당신은 잘못된 전제를 가지고 질문하고 있습니다. ==연산자는 메모리 위치를 비교하지 않기 때문에 메모리 위치 자체 를 변경하는 것은 현명하지 않습니다 . ==참조인가 작업자는 상기 비교 신원 없이 어떻게 JVM 구현 그것을 상기 언급 된 개체를.

일반적인 이해에 반하는 예를 들자면, 분산 된 JVM은 로컬 복사 가능성을 포함하여 다른 컴퓨터의 RAM에 개체를 보유 할 수 있습니다. 따라서 단순히 주소를 비교하는 것은 작동하지 않습니다. 물론 Java 언어 사양에 정의 된 의미가 변경되지 않도록하는 것은 JVM 구현에 달려 있습니다.

특정 JVM 구현 구현할 경우 직접 개체의 메모리 위치를 비교하여 기준을 비교 하고 , 물론 메모리 위치를 변경할 수 가비지 콜렉터를 갖는다는, 이들 두 기능은 서로 간섭되지 않도록하기 위해 JVM의 몫 호환되지 않는 방법.

If you are curious on how this can work, e.g. inside optimized, JIT compiled code, the granularity isn’t as fine as you might think. Every sequential code, including forward branches, can be considered to run fast enough to allow to delay garbage collection to its completion. So garbage collection can’t happen at any time inside optimized code, but must be allowed at certain points, e.g.

  • backward branches (note that due to loop unrolling, not every loop iteration implies a backward branch)
  • memory allocations
  • thread synchronization actions
  • invoking a method that hasn’t been inlined/analyzed
  • maybe something special, I forgot

So the JVM emits code containing certain “safe points” at which it is known, which references are currently held, how to replace them, if necessary and, of course, changing locations has no impact on the correctness. Between these points, the code can run without having to care about the possibility of changing memory locations whereas the garbage collector will wait for code reaching a safe point when necessary, which is guaranteed to happen in finite, rather short time.

But, as said, these are implementation details. On the formal level, things like changing memory locations do not exist, so there is no need to explicitly specify that they are not allowed to change the semantics of Java code. No implementation detail is allowed to do that.


I understand you are asking this question after someone says it behaves that way, but really asking if it does behave that way isn't the right approach to evaluating what they said.

What you should really be asking (primarily yourself, others only if you can't decide on an answer) is whether it makes sense for the GC to be allowed to cause a comparison to fail that logically should succeed (basically any comparison that doesn't include a weak reference).

The answer to that is obviously "no", as it would break pretty much anything beyond "hello, world" and probably even that.

So, if allowed, it is a bug -- either in the spec or the implementation. Now since both the spec and the implementation were written by humans, it is possible such a bug exists. If so, it will be reported and almost certainly fixed.


No, because that would be flagrantly ridiculous and a patent bug.

The GC takes a great deal of care behind the scenes to avoid catastrophically breaking everything. In particular, it will only move objects when threads are paused at safepoints, which are specific places in the running code generated by the JVM for threads to be paused at. A thread at a safepoint is in a known state, where the positions of all the possible object references in registers and memory are known, so the GC can update them to point to the object's new address. Garbage collection won't break your comparison operations.


Java object hold a reference to the "object" not to the memory space where the object is stored.

Java do this because it allow the JVM to manage memory usage by its own (e.g. Garbage collector) and to improve global usage without impacting the client program directly.

As instance for improvement, the first X int (I don't remember how much) are always allocated in memory to execute for loop fatser (ex: for (int i =0; i<10; i++))

And as example for object reference, just try to create an and try to print it

int[] i = {1,2,3};
System.out.println(i);

You will see that Java returning a something starting with [I@. It is saying that is point on a "array of int at" and then the reference to the object. Not the memory zone!

참고URL : https://stackoverflow.com/questions/34739112/can-the-jvm-gc-move-objects-in-the-middle-of-a-reference-comparison-causing-a-c

반응형