Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

static, metaspace, 바이너리호환성 (곽민주) #175

Open
MinjuKwak01 opened this issue May 29, 2024 · 0 comments
Open

static, metaspace, 바이너리호환성 (곽민주) #175

MinjuKwak01 opened this issue May 29, 2024 · 0 comments

Comments

@MinjuKwak01
Copy link
Collaborator

정적 메소드

  • 클래스로 객체를 생성하지 않아도 호출이 가능한 메소드
  • 해당 메소드가 정의되어있는 클래스 이름이나 생성된 객체를 통해 호출될 수 있다.
public class Test {
    public static void sm() {
        System.out.println("this is static method!");
    }
    
    public void m() {
        System.out.println("this is non-static method!");
    }
}

public class Main {

    public static void main(String[] args) {
        Test.sm(); // O
        Test.m(); // X
    }
}

말 그대로 "정적"이기 때문에 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성된다.

정적 메소드는 무슨 기준으로 정해야 하는지?

  1. 변화를 가정하지 않는다.
    • 동작이 인스턴스 상태에 의존하지 않고, 고정된 로직을 수행
  2. 메소드가 인스턴스 변수를 사용하지 않는다.
   public class ClassA { // 정적 메소드: 인스턴스 변수 사용 안 함 
         public static int add(int a, int b) { return a + b; }
   }
  1. 인스턴스 생성에 의존하지 않는다.
  2. 메소드가 공유되고 있다면, 정적 메소드로 추출해낼 수 있다.

정적 변수가 다른 객체를 참조한다면?

static Object obj = new Object();
  • obj는 Metaspace에 저장, Object 클래스의 객체는 heap영역에 적재된다.
Metaspace 영역?
image
  • Java 8 부터 새로 생긴 영역으로 heap의 PermGen영역이 담당하던 부분을 대체하고, 힙 영역에서 네이티브 메모리 영역으로 이전하였다.
    • Native Memory란?
      • JVM이 직접 관리하지 않는다.
      • 운영체제가 시스템 콜을 통해 메모리를 애플리케이션에 할당.
        • 개발자가 영역 확보의 상한을 크게 의식할 필요가 없어지게 되었다.
      • 예로 JNI, Direct Buffers, Metaspace
    • Heap 영역에는 Young Generation, Eden, Old Generation 영역만 남는다.
  • Java class 들이 런타임에 알아야 할 모든 정보를 갖고 있다. (클래스, 메소드 정보 등등)
  • 제한된 크기를 갖고 있지 않다. (OutOfMemoryError의 위험이 줄어든다.)
  • Method Area와 뭐가 다르지?
    • Method Area의 구현체 중 하나

정적 클래스

public class OuterClass { 

	public static class StaticInnerClass { 
		public void display() {
		}
	}
}

+.. inner 클래스에 static을 꼭 붙여줘야 하는 이유?

  • 내부 클래스(static 이 아닐때)는 외부 클래스의 인스턴스화 이후 내부 클래스의 인스턴스화가 가능하다.
  • 두 인스턴스의 관계 정보는 내부 인스턴스 안에 만들어져, 메모리 공간을 더 차지하고 생성시간도 더 걸린다.
  • 더 심각한 문제는 내부 클래스가 외부 클래스 인스턴스에 대한 참조를 갖고 있기 때문에
    • GC는 외부 클래스의 인스턴스를 수거 대상으로 보지 않아서 GC의 대상에서 빠진다.
    • 내부 클래스 데이터가 살아있는 한 외부 클래스 데이터도 계속 살아있어서 메모리에 데이터가 쌓인다.
    • 따라서 외부 클래스를 참조할 일이 없다면, 중첩 클래스는 static을 붙이자.

바이너리 호환성

현대의 자바 개발 도구는 automatic recompilation(자동 재컴파일)을 지원하지만, 인터넷 환경과 같이 분산된 환경에서는 이러한 작업이 힘들다.

바이너리 호환성

  • 변경 이후에도 컴파일을 하지 않고 바이너리를 실행할 수 있는 특성
  • 뭔가를 바꾼 이후에도 에러 없이 기존 바이너리가 실행될 수 있는 상황

바이너리 호환성의 존재 이유

  • 자바 언어의 특성을 나열하면 항상 OS에 독립적이다 라는 특성을 말하곤 한다.
  • 인터넷이 도입되고 분산구조가 확장됨과 동시에 다양한 하드스펙의 컴퓨터들이 생산되기 시작했고, 여러명이서 개발을 하거나 개발과 운영의 자원이 다른 환경에서 문제들이 생겨나기 시작했다.
    • 정상적인 동작을 위해 OS 변경에 따라서도 매번 컴파일이 필요했다.
    • 하지만 자바는 JVM이라는 가상환경을 OS위에 올림으로서 컴파일을 반복하지 않고 동작할 수 있도록 설계되었다.
    • 이러한 고민들이 OS뿐만 아니라 코드의 변경에도 적용이 된 것

바이너리 호환성이 적용되는 경우

  1. 기존 메소드, 생성자, 초기화 블록을 성능 향상을 위해 재구현하는 것
  2. 패키지 전체가 업데이트될 때, 기본 접근 제어자(package-private)를 가진 필드, 메소드, 생성자를 삭제하는 경우
  3. 기존 클래스나 인터페이스에 새로운 필드, 메소드 또는 생성자를 추가하는 경우
  4. 클래스 계층 구조에서 메소드를 상위 클래스로 이동하는 경우

등등등...
=> 메소드 시그니처의 변경이나, 클래스 인터페이스의 변경이나 접근 제어 변경이 없고 기존 기능을 유지한다.

이펙티브 자바 20 -> 추상 클래스보다는 인터페이스를 우선하라

인터페이스 장점

  • 계층 구조가 없는 타입을 만들 수 있다.
  
  public interface Walkable {  
	void walk();  
}  
public interface Flyable {  
	void fly();  
}  
public class Penguin implements Walkable,Flyable{  
	@Override  
	void walk() {}  
  
	@Override  
	void fly() {}  
}
  • 믹스인 정의에 안성맞춤
    • 믹스인이란 : 특정 기능을 여러 클래스에 재사용할 수 있도록 하는 프로그래밍 패턴
  • 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다.
    • 기존 클래스에 새로운 추상 클래스를 끼워 넣기는 어렵다.
    • 두 클래스가 같은 추상 클래스를 확장하려면, 그 추상 클래스는 계층 구조상 두 클래스의 공통 조상이어야 한다.
abstract class Animal {
   public abstract void makeSound();
}

class Dog extends Animal {
   @Override
   public void makeSound() {
       System.out.println("Woof");
   }
}

class Cat extends Animal {
   @Override
   public void makeSound() {
       System.out.println("Meow");
   }
}

// 새로운 추상 클래스 추가
abstract class Robot {
   public abstract void performTask();
}

class DogRobot extends Dog {
   @Override
   public void performTask() {
       System.out.println("DogRobot performs task");
   }
}

class CatRobot extends Cat {
   @Override
   public void performTask() {
       System.out.println("CatRobot performs task");
   }
}

DogRobot이랑 CatRobot은 Dog와 Cat을 상속하면서도 Robot의 기능을 가져와야 하는데, 그러면 상속 구조가 복잡해지고 코드의 가독성과 유지보수성이 떨어진다.

-> 인터페이스로 역할 분리, 상속 구조 단순화

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant