[Java] JVM 메모리 구조로 보는 자바의 본질 / 객체지향의 특성별 예시

2026. 4. 6. 11:34·Java

 

자바를 공부하다 보면 수많은 문법과 규칙들을 마주하게 됩니다.

처음에는 단순히 개념을 암기하기 바쁘지만, 그 안의 배경과 필요성을 들여다보면 이유 없는 규칙은 하나도 없다는 것을 깨닫게 됩니다.

특히 개발자로서 견고하고 효율적인 코드를 설계하기 위해서는 암기식 문법이 아닌 '본질'을 이해해야 합니다.

오늘은 자바가 어떻게 동작하는지 그 근본적인 원리부터 시작해, 객체지향의 4가지 특성이 가지는 진짜 의미를 정리해 보려 합니다.

 


 

자바는 왜 '컴파일+인터프리터' 언어일까? 그리고 혁신적인 JVM 메모리 구조

 

자바의 가장 큰 특징 중 하나는 컴파일과 인터프리터 방식을 혼합하여 사용한다는 점입니다.

우리가 작성한 소스 코드(.java)는 먼저 컴파일러에 의해 바이트 코드(.class)로 변환됩니다.

이 바이트 코드를 JVM이 한 줄씩 읽어(인터프리터) 운영체제가 이해할 수 있는 기계어로 번역하여 실행합니다.

 

이러한 컴파일 → 실행 → JVM 실행의 흐름  덕분에 자바는 운영체제에 독립적으로 실행될 수 있습니다.

 

그리고 이 프로그램이 실행될 때, JVM이 메모리 공간을 어떻게 확보하고 나누어 쓰는지를 아는 것은 자바 이해의 핵심입니다.

변수를 어디에 선언하느냐는 단순한 취향 차이가 아니라, 변수가 사는 '영역'과 '수명'을 결정짓는 중요한 설계이기 때문입니다.

 

JVM 메모리 구조

  • Method Area (메서드 영역) - 클래스 변수 (static)
    • 클래스가 로딩될 때 클래스 정보와 함께 저장됩니다.
    • 프로그램이 끝날 때까지 유지되며, 같은 클래스의 모든 인스턴스가 값을 공유합니다. (예: 모든 학생 객체가 공유하는 '학교 이름')
    • 접근: 클래스명.클래스 변수명
  • Heap Area (힙 영역) - 인스턴스 변수
    • new 키워드를 통해 동적으로 인스턴스가 생성될 때마다 이 영역에 방을 잡습니다.
    • 인스턴스마다 각기 다른 고유한 값을 저장할 때 사용합니다. (예: 각 학생의 '이름')
    • 접근: 참조 변수명.인스턴스 변수명
  • Call Stack (스택 영역) - 지역 변수
    • 메서드가 호출될 때 메모리가 할당되고, 메서드 종료 시 함께 소멸하는 짧은 생명주기를 가집니다.
    • 주의: 재귀 호출 시 종료 조건을 명시하지 않으면 이 Call Stack에 메서드가 무한히 쌓여 StackOverflowError가 발생합니다.

이 구조를 이해하고 나면, 무작정 사용하던 static을 언제 써야 하는지, 각 변수들이 어떤 순서로 메모리에 올라가는지 그 차이점을 명확히 알 수 있습니다.

 


객체지향의 특성: "왜 이렇게 설계해야 할까?"

"복잡성을 어떻게 쪼개고 유연하게 연결할 것인가"

 

자바의 객체지향 프로그래밍의 본질를 통해 유지보수성과 확장성을 높입니다.

 

① 캡슐화 (Encapsulation): private의 본질적인 용도

흔히 캡슐화를 '데이터 숨기기'라고 배우지만 그 본질은 객체의 무결성을 보호하고 메모리를 최적화하는 것입니다.

  • 데이터 보호 (변수 숨기기)
    • 변수에 직접 접근해 형식에 맞지 않는 값(예: 시간에 30시를 입력)이 들어오는 것을 막습니다.
    • private으로 직접 접근을 차단하고, setter 메서드를 통해 유효성을 검사한 뒤 안전한 값만 필터링하여 저장합니다.
  • 메모리 최적화 (생성자 숨기기)
    • 생성자를 private으로 선언하면 외부에서 new를 통해 여러번 객체를 생성하는 것을 막을 수 있습니다.
    • 시스템 내에서 단 하나의 객체만 생성해 공유하는 싱글톤(Singleton) 패턴이나, 대신 객체를 생성해 주는 팩토리 메서드로 이어져 메모리 낭비를 줄여줍니다.
    • (예: JVM에 접근하는 유일한 객체인 Runtime도 싱글톤으로 설계)

 

② 상속(Inheritance)과 추상화(Abstraction): 클래스와 인터페이스의 차이

상속과 추상화는 모두 코드를 재사용하고 확장한다는 점에서 비슷해 보이지만, 본질적인 목적이 다릅니다.

  • 상속 (클래스, extends)
    • 본질: 부모 클래스의 100% 완성된 멤버와 코드를 재사용하고 확장합니다.
    • 특징: ~은 ~이다 (is-a) 관계일 때 사용하며, 부모의 기능을 물려받아 그대로 쓰거나 오버라이딩을 통해 덧붙여 사용합니다.
  • 추상화 (인터페이스/추상클래스)
    • 본질: 여러 클래스의 공통적인 설계도를 제공하고, 자식 클래스에게 구체적인 구현을 강제하는 것입니다.
    • 추상 클래스 vs 인터페이스:
      • 추상 클래스: 완성된 기능(상속)과 구현이 필요한 미완성 기능(추상 메서드)이 혼합된 설계도입니다.
      • 인터페이스 (implements): 상태(멤버 변수)를 가지지 않고 오직 100% 추상 메서드와 상수만으로 이루어진 '기능의 자격증'입니다. 
        • 그래서 기능을 할 수 있다는 의미의 ~able로 끝나는 인터페이스가 많습니다.

 

③ 다형성 (Polymorphism): 결합도를 낮추는 마법

다형성은 부모 타입의 참조 변수로 자식 타입의 객체를 다룰 수 있는 능력입니다.

이를 활용하면 코드의 수정 없이도 부품을 갈아 끼우듯 객체를 유연하게 변경할 수 있습니다.

 

단계별 객체 간의 결합도를 낮추는 과정

  • 1단계 (강한 결합)
    • SamsungTV와 LgTV 클래스가 독립적으로 존재할 때, TV를 교체하려면 참조 변수 타입부터 인스턴스, 메서드 호출부까지 전부 수정해야 합니다.
  • 2단계 (인터페이스 적용)
    • 공통 기능을 TV 인터페이스로 묶어 상속시킵니다.
    • 참조 변수를 TV 타입으로 통일하면, 다형성 덕분에 TV tv = new SamsungTV();에서 new LgTV();로 객체 생성 부분만 바꾸면 나머지 코드는 수정할 필요가 없습니다.
  • 3단계 (Factory 패턴 적용)
    • 팩토리 클래스가 입력값에 따라 알맞은 TV 객체를 생성해 TV 타입으로 반환해 줍니다.
    • 이제 메인 로직에서는 어떤 TV 클래스가 추가되거나 변경되더라도 코드를 전혀 수정할 필요가 없습니다.
  • 4단계 (Spring Boot 활용)
    • 프레임워크가 @Component, @Autowired 등을 통해 상황에 맞는 객체를 스스로 찾아 주입해 줍니다.
    • 개발자는 객체의 생성과 의존성 관리를 프레임워크에 맡기고 비즈니스 로직에만 집중할 수 있게 됩니다.

정리하며

"그냥 문법이니까" 적용하는 것을 넘어, 기술이 탄생한 목적과 배경을 파고드는 과정은 매우 흥미롭습니다.

이를 통해 내가 쓴 코드를 설명할 수 있는 개발자가 될 수 있기 때문입니다.

JVM 메모리의 동작 방식을 알면 왜 그렇게 변수를 선언해야 하는지 논리적인 이유가 생기고, 인터페이스와 제네릭, 컬렉션을 조합해 보며 불필요한 형변환과 에러를 예방하는 안전한 설계를 체감할 수 있었습니다.

특히, 다형성과 상속을 통해 강하게 얽혀있던 코드의 결합도를 점진적으로 해소해 나가는 과정을 보며, 유지보수성과 확장성이 높은 코드를 작성하는 개발의 방향성을 잡을 수 있었습니다.

 

 

 

출처

https://s-bug.tistory.com/57#google_vignette

https://gptjs409.github.io/java/2019/09/04/jvm.html

https://s-bug.tistory.com/57#google_vignette

저작자표시 (새창열림)

'Java' 카테고리의 다른 글

[Spring Boot] 조회와 수정의 분리(같이 했을 때 문제점과 리팩토링)  (0) 2026.05.17
[JDBC] JDBC의 배경 / JDBC 인터페이스와 메서드 / 동작원리  (0) 2026.04.06
[Java] 배열 / 2차원 배열 / 가변 배열  (0) 2026.03.20
[Java] println(char[ ] 배열명)에서 주소 값이 아닌 텍스트가 나오는 이유  (0) 2026.03.19
[Java] 자바의 원칙 vs 편의성: 철학을 깬 복합 대입 연산자, char 배열 출력과 철학을 지킨 for-each문  (0) 2026.03.19
'Java' 카테고리의 다른 글
  • [Spring Boot] 조회와 수정의 분리(같이 했을 때 문제점과 리팩토링)
  • [JDBC] JDBC의 배경 / JDBC 인터페이스와 메서드 / 동작원리
  • [Java] 배열 / 2차원 배열 / 가변 배열
  • [Java] println(char[ ] 배열명)에서 주소 값이 아닌 텍스트가 나오는 이유
미우딩
미우딩
다양한 프로그래밍 언어를 공부하고 있는 공대생입니다! Python, Java, C언어를 주로 공부하고 있으며 Figma를 통한 디자인도 종종 올릴 예정입니다.
  • 미우딩
    미우의 코딩 기록
    미우딩
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • Java (47)
      • C언어 (0)
      • 대외활동 (5)
      • Spring (4)
      • 프로젝트 (2)
      • 앱개발 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    IT #알고리즘 #코딩테스트 #DFS
    Spring #정적컨텐츠 #MVC #템플릿엔진 #API #애너테이션
    IT #알고리즘 #코딩테스트 #삽입정렬 #오름차순정렬
    IT #백준 #알고리즘 #코딩테스트 #배열 #형변환 #아스키코드
    push #pop #StringBuffer
    IT #BFS #코딩테스트 #구현 #큐 #재귀함수
    IT #알고리즘 #코딩테스트 #퀵정렬 #오름차순정렬
    IT #코딩테스트 #알고리즘 #슬라이딩윈도우 #투포인터 #함수
    android #java #kotlin #compose
    IT #알고리즘 #코딩테스트 #우선순위큐 #힙 #절댓값 #오름차순 #정렬
    IT #큐 #알고리즘 #코딩테스트 #자바 #선입선출
    IT #코딩테스트 #알고리즘 #백준 #투포인터 #배열
    IT #코딩테스트 #이진탐색 #알고리즘
    IT #백준 #코딩테스트 #알고리즘 #구간합 #반복문
    IT #알고리즘 #코딩테스트 #선택정렬 #내림차순정렬 #최댓값
    IT #알고리즘 #그리디알고리즘 #탐욕알고리즘 #코테
    android #java #kotlin #상태관리
    IT #코딩테스트 #알고리즘 #스택 #
    java #android #개발자
    IT #코딩테스트 #알고리즘 #DFS #재귀함수 #무방향그래프
    IT #백준 #알고리즘 #코딩테스트 #배열 #형변환 #평균구하기
    BufferedReader
    IT #코딩테스트 #알고리즘 #투포인터 #시간제한 #Scanner #BufferedReader #StringTokenizer
    IT #코딩테스트 #알고리즘 #버블정렬 #오름차순
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
미우딩
[Java] JVM 메모리 구조로 보는 자바의 본질 / 객체지향의 특성별 예시
상단으로

티스토리툴바