객체변수는 왜 주소값이 아닌 참조값을 받는지요?
Java를 공부하던중 몇가지 의문이 있어 이렇게 질문드려 봅니다.
질문은 자바에서 객체변수는 왜 직접메모리 상의 주소를 갖지않고 해시코드 라는
참조값을 쓰는지 그것이 궁금합니다.
제가 알고 있기로는 자바는 원소스 코드를 .class 란 바이트 코드로 바꿔서 JVM 이 이를 다시 컴파일 하여 OS 에 종속되지 않는다고 알고 있습니다.
C 와는 달리 객체변수란 것도 주소값을 갖는게 아니라 JVM 에서 주소값에
대응하는 해시코드를 생성하여 이를 참조값으로 넘긴다고 알고있습니다.
이는 OS 마다 주소를 지정하는 방식 및 번지체계가 다르기 때문에 각 OS에
에 종속되지 않게 하기 위함이라고 합니다.
여기까지는 제가 알고있는 내용입니다.
근데 전 이상한게 왜 굳이 변수에 주소값을 넘기지 않고 참조변수라는 해시코드 값을 넘기느냐 하는것입니다. 어차피 JVM 이란 OS 상에서 구동되는 것이므로 OS 마다 주소지정 방식및 번지체계가 틀리더도 JVM 이 구동하면서 지정된 주소값을 넘기면 되지 않나 하는 생각입니다. 어차피 윈도우나 유닉스에서 작성한 class 를 JVM에서 구동시키면서 기계어로 컴파일 됨으로 즉 JVM 에서 주소를 지정해 주는데 굳이 한단계 더 거치는 해시코드로 만들 이유가 없다고 생각되는데요 다른 이유가 있는 것인지요?
그리고 한가지 더 궁금한것은 그럼 왜 기본형 변수는 참조값으로 하지 않나여?
이것역시 OS 마다 주소지정 방식이라든지 그런것이 틀릴텐데 굳이 객체형변수만 참조값으로 하고 기본형 변수는 참조값으로 넘기지 않는것이 궁금합니다. 이 역시 플렛폼에 독립적으로 하려면 윈도우나 유닉스나 주소를 지정하는 방법이 틀리다면 이역시 JVM 에서 주소를 지정하고 그를 다시 해시코드를 생성하여 넘겨야 하지 않나 생각됩니다.
마지막으로 변수를 선언하면 무조건 심볼테일블 이란곳에서 변수로 명명된 것과 실제 메모리상의 주소값을 매핑 시켜주는걸로 알고 있습니다. 그럼 객체변수는 2단계를 거쳐서 객체에 접근할수 있는건지요? 제가 알기론 JVM 메모리 구조중 PC 레지스터 란 곳에서 주소를 기억하고 있는걸로 알고 있는데
그 심볼 테이블에 먼저 접근해서 그곳에서 PC 레지스터로 또 접근하여 객체에 접근하는 것인지요?
즉 Sun a = new Sun(); 했을 경우
a란 객체에 접근하는 순서가 변수 a 가 가리키는 심볼테이블에서 a 로 연결되어있는 주소로 찾아가 그곳에 있는 정수형인 참조변수 즉 참조값(해시코드) 가 가리키는 해시테이블(Index Table) 로 가서 그곳에 해시값이 연결된 주소로 찾아가서 객체에 접근하게 된다고 생각됩니다. 제 생각이 맞는지요?
Java의 기초를 다시 보다보니 첨에 Java를 공부할때 의문을 갖지 못했던 것들이 이제서야 궁금해 집니다.
알고자 하는 욕심에 이렇게 염치없이 질문만 드립니다.
그럼 답변 부탁드리겠습니다.
제 생각입니다
Java Virtual Machine spec 에는 class file, bytecode instruction format이 정의되어 있습니다. 아마도 가장 확실한 대답은 JVM spec을 참조하시면 될 것입니다.
그럼 질문하신 내용에 대한 제 생각을 말씀드리겠습니다.
첫번째,
Java의 object가 hash로 표현된다는 것은 잘못 알고 계신 듯 합니다.
우선 hash:object는 1:1 mapping이 아닙니다. 1:N이지요.
bytecode에서 object의 type은 reference type입니다. spec에는 단지 reference type이 4byte라는 정의 정도만 있습니다. 이를 실제 어떻게 구현하는 지는 VM 개발자의 몫입니다.
예를 들어, 실제 object가 저장되어 있는 메모리 주소(32bit machine에서는 4byte)를 직접 사용할 수도 있고, garbage collection 등의 문제 때문에 indirect referencing을 할 수도 있습니다.
그리고, bytecode에서 object의 주소 또는 reference의 실제 값은 어디에도 없습니다. Platform independency를 지켜주기 위함이죠. 따라서 동일한 .class 파일이 Windows, Unix, Linux 등 여러 플랫폼에서 동작할 수 있습니다. 어차피 machine code로 변환(JIT compiler가 있을 경우)된다고 해서 .class file에 machine code를 넣어 두면 Platform independency가 깨질 것입니다.
두번째,
두번째 질문은 잘 이해가 안됩니다만, Java primitive type 역시 spec에 정의되어 있습니다. int(4 byte), long(8byte), float(4byte, IEEE-754) 등등으로 정의되며, bytecode에서의 primitive constant들은 Big endian으로 표현됩니다.
세번째,
전반적으로 spec을 보시면 도움이 되실 듯 합니다. bytecode의 모든 operation은 symbol을 토대로 이루어집니다. 이는 dynamic loading을 가능하게 해줍니다. 실제 JVM spec에서는 Unix에서처럼 symbol table이라 하지 않고 Constant pool이라고 부릅니다. primitive constant, String constant, Class constant, Field constant 등 모든 것이 symbol로 표현됩니다. 이러한 symbol들을 실제 value 또는 reference 등으로 연결시켜 주는 것은 VM의 몫이 맞습니다.
예를 들어 말씀하신 Sun a = new Sun();의 경우 bytecode로는 다음과 같이 표현됩니다.
위의 bytecode에서 보시는 것처럼 역시 주소는 어디에도 없습니다. PC register는 program counter, 즉 현재 실행되는 bytecode instruction pointer를 의미합니다. 물론 virtual machine이니까 virtual한 개념입니다. VM은 이를 real machine에서 구현해 주어야 하는 것입니다.
PC register는 constant pool과는 관계가 없습니다. Interpreter 방식을 예로 들면, 위의 bytecode에서 첫번째 instruction인 new를 보면 "Sun"이라는 Symbol에 해당하는 constant pool index가 나오면(즉 bytecode 자체에 string이 들어가 있는 것이 아닙니다. 위의 코드에는 편의상 "Sun"이라고 쓴 것입니다) 이를 현재 class의 constant pool을 통해 "Sun"이라는 string을 얻어옵니다. 그럼 VM은 "Sun"이라는 이름의 클래스를 loading하거나 이미 loading된 table에서 찾아서 그의 reference를 stack top에 push하는 것입니다. 이 때 stack에 push된 reference는 뒤에서 실제 constructor를 호출하는 invokespecial instruction과 local array에 저장하는 astore instruction에서 사용되는 것입니다.
장황하게 쓴 감이 있습니다만, 조금이라도 이해하시는 데 도움이 되면 좋겠습니다.
코더에서 프로그래머까지다
코더에서 프로그래머까지
제가 지금 가지고 있는 궁금증과 거의 일치합니다
자바를 공부한지 얼마 되지 않은 학생입니다. 질문자님과 동일한 궁금증을 가지고 있는데 하나 있는 답변이 이해하기 더 어려워서.... 도움이 전혀 안되네요ㅜㅜ 10년도 더 지난 질문이지만 조금 더 쉬운 답변이 혹시나 더 올라올까 해서 댓글 달아봅니다.
딴소리를 써놨었네요근데 이거 왜 삭제가 안됌
딴소리를 써놨었네요
근데 이거 왜 삭제가 안됌
댓글 달기