객체변수는 왜 주소값이 아닌 참조값을 받는지요?

이상준의 이미지

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를 공부할때 의문을 갖지 못했던 것들이 이제서야 궁금해 집니다.
알고자 하는 욕심에 이렇게 염치없이 질문만 드립니다.

그럼 답변 부탁드리겠습니다.

hultul의 이미지

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로는 다음과 같이 표현됩니다.

new "Sun"  // "Sun"이라는 이름의 클래스를 로딩합니다.(아직 로딩되지 않았다면)
dup  // stack top element를 복사합니다.
invokespecial "Sun()"  // stack top element에 들어 있는 object에서 "Sun()" 이름의 constructor method를 호출합니다.
astore_3  // stack top element를 local variable array index 3에 저장합니다(Java compiler가 변수 a를 local index 3에 저장되도록 compile 했다면)

위의 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에서 사용되는 것입니다.

장황하게 쓴 감이 있습니다만, 조금이라도 이해하시는 데 도움이 되면 좋겠습니다.

코더에서 프로그래머까지다

코더에서 프로그래머까지

이전형@Google의 이미지

자바를 공부한지 얼마 되지 않은 학생입니다. 질문자님과 동일한 궁금증을 가지고 있는데 하나 있는 답변이 이해하기 더 어려워서.... 도움이 전혀 안되네요ㅜㅜ 10년도 더 지난 질문이지만 조금 더 쉬운 답변이 혹시나 더 올라올까 해서 댓글 달아봅니다.

jehnpark의 이미지

딴소리를 써놨었네요
근데 이거 왜 삭제가 안됌

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.