책 정리/Java의 정석

Java의 정석 빼먹은 개념 재정리

긍.응.성 2021. 12. 7. 01:25
반응형

Spring으로 업무를 수행하지만, Java라는 언어에 대해 중간중간 구멍이 나있다고 느껴 이 책을 다시 보게 되었다. 이곳에 기록하는 것들은 정말 소소하지만 내 머릿속에 잘 들어오지 않았던 것들이거나, 잘 모르고 있어 유심히 보게 된 것들에 대한 것이다.

JDK, JRE, JVM

  • JDK : Java Development Kit (자바 개발 도구)
    • JRE + 개발에 필요한 실행파일(jdk bin 디렉터리엔 javac.exe, java.exe, javap.exe 등의 실행파일이 존재한다)
  • JRE : Java Runtime Environment (자바 실행 환경)
    • JVM + 클래스 라이브러리 (Java API)
  • JVM : Java Virtual Machine (자바 가상 머신)

JVM의 메모리 구조

메서드 영역 (method area)

프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스 파일(*.class)을 읽어 분석하여 클래스에 대한 정보(class data)를 이곳에 저장한다. 클래스의 클래스 변수(static)도 이 영역에 함께 생성된다.

힙 (heap)

인스턴스 변수가 생성되는 공간이다. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 저장된다.

호출 스택 (call stack)

메서드에 필요한 메모리 공간을 제공하는 곳이다. 메서드 작업을 수행하는 동안 지역변수와 연산의 중간 결과를 저장한다.

클래스 변수의 성능상 이점

인스턴스 변수, 지역 변수로 선언하여 사용하는 것 보다 클래스 변수로 선언해서 참조하는 것이 성능상 이점을 갖는다. 힙 영역에서 인스턴스를 찾아 변수를 타고 들어갈 필요 없이 메서드 영역에서 빠르게 찾을 수 있기 때문이다.

java.lang 패키지

equals 메서드

  • 객체의 참조 변수를 받아서 비교하며 그 결과를 boolean 값으로 알려준다.
public boolean equals(Object obj) {
        return (this == obj)
}
  • 객체를 생성할 때 메모리의 비어있는 공간을 찾아 생성하므로, 서로 다른 두 객체가 같은 주소를 갖는 것은 있을 수 없는 일이다.
    • 하지만, 두 개 이상 참조 변수가 같은 주소 값은 갖는 것은 가능하다.

hashCode 메서드

  • hashing 기법이 사용되는 해시함수를 구현한 메서드.
  • 일반적으로 hashCode가 같은 두 객체가 존재하는것은 가능하다
  • Object 클래스에 정의된 hashCode 메서드는 객체의 주소 값을 이용하여 hashCode를 만들어 반환한다
    • 그렇기에 Object의 hashCode를 오버라이드 하지 않는 한에서, 한 번의 실행에서 서로 다른 두 객체는 결코 같은 hashCode를 가질 수 없다
  • 클래스의 멤버 변수 값으로 객체의 같고 다름을 판단해야 하는 경우 hashCode를 적절히 오버라이드 해야 한다.
    • 같은 객체라면 hashCode 메서드를 호출했을 때의 해시코드도 같아야 하기 때문이다.
  • 참고로 해싱 기법을 사용하는 HashMap이나 HashSet같은 클래스에 저장할 객체라면 반드시 hashCode 메서드를 오버라이딩 해야한다
    • HashSet, HashMap은 서로 다른 두 객체에 대해 equals메서드와 hashCode의 반환값이 같아야 같은 객체로 인식한다. hashCode를 구현하지 않으면 equals의 결과가 true지만 hashCode가 달라 같은 객체를 중복하여 저장하는 상황이 발생할 수 있다.

Vector, HashTable

실무에 잘 사용하지 않지만 레거시 라이브러리에서 많이 본 자료구조이다...

  • 컬렉션 프레임워크에서 List의 구현체로 Vector, ArrayList, Map의 구현체로 HashTable, HashMap이 존재한다.
  • ArrayList와 HashMap을 주로 사용하는데 이는 Vector와 HashTable이 동기화 처리가 되어있기 때문이다.
  • Vector와 HashTable은 JDK 1.2 이전에 나온 구버전의 클래스들이다. 이들은 멀티쓰레딩 환경에 데이터 일관성을 보장할 수 있도록 동기화 처리가 되어있었지만, 멀티쓰레드 환경이 아닌 곳에서 성능을 떨어 뜨리는 요인이 되어 현재는 잘 사용되지 않는다.
  • 멀티쓰레드 환경에서 사용한다면 java.util.Collections클래스에서 제공하는 동기화 메서드를 이용하자

멀티쓰레드

Thread 클래스의 start()와 run()

  • start() : 현재 start가 호출된 call stack이 아닌 새로운 call stack을 생성하며, 해당 call stack에서 Runnable을 run 한다.
  • run() : 현재 call stack에서 바로 할당한다.

ThreadGroup

  • 서로 관련된 쓰레드를 그룹으로 묶어 다룰 수 있다.
  • 그룹을 지정하지 않으면 main 쓰레드 그룹에 속한다
  • 쓰레드 우선순위는 자신을 생성한 부모 쓰레드 그룹의 우선순위로 부터 상속받는다.

데몬(Daemon) 쓰레드

  • 일반 쓰레드의 작업을 돕는 보조 쓰레드의 역할을 갖는다.
  • 일반 쓰레드가 모두 종료되면 자동으로 종료된다.
    • Ex. 가비지 컬렉터, 자동 저장, 자동 리프레시 등에 사용
  • setDaemon(boolean on)으로 설정 가능하며, start() 호출 전 설정하여야만 한다.

synchronized (쓰레드 동기화)

  • 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서 데이터의 일관성을 유지하는 것

특정 객체 lock

synchronized(객체 참조변수) { ... }

메서드 lock

public synchronized void methodName() {
	...
}

// this로 해당 객체에 대하여 methodName 접근시 락을 걸기에 위의 메서드 lock과 동일하게 동작한다.
public void methodName() {
    synchronized(this) {
        ...
    }
}

wait(), notify(), notifyAll()

실제로 사용할 일은 거의 없지만... Object 클래스에 정의되어 있으며, 동기화 블록 내에서만 사용 가능하다.

  • wait() : 객체에 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣는다
  • notify() : waiting pool에서 대기 중인 쓰레드 중 하나를 깨운다. 깨어나는 쓰레드의 순서는 보장되지 않는다.
  • notifyAll() : waiting pool에서 대기중인 모든 쓰레드를 깨운다.

volatile - cache와 메모리 간의 불일치 해소

  • 성능 향상을 위해 변수의 값을 core의 cache에 저장해 놓고 작업한다
  • 여러 쓰레드가 공유하는 변수에는 volatile을 붙여야 항상 메모리에서 값을 읽어온다.

입출력

바이트 기반 스트림

  • InputStream, OutputStream : 바이트기반 입출력 스트림의 최고 조상
  • ByteArrayInput/OutputStream : 바이트 배열에 데이터를 입출력하는 바이트 기반 스트림
  • FileInput/OutputStream : 파일에 데이터를 입출력하는 바이트기반 스트림

바이트기반 보조 스트림

  • FilterInput/OutputStream : 모든 바이트 기반 보조 스트림의 최고 조상
  • BufferedInput/OutputStream : 입출력 효율을 높이기 위해 버퍼를 사용하는 보조 스트림
    • default로 8MB의 크기의 버퍼를 가짐
  • DataInput/OutputStream : 기본형 단위(primitive type)로 읽는 보조 스트림
  • SequenceInputStream : 여러 입력 스트림을 연결하여 하나의 스트림으로 다루도록 하는 스트림
  • PrintStream : 데이터를 다양한 문자로 출력하는 기능을 제공하는 보조 스트림
    • System.out과 System.err가 PrintStream에 해당
    • PrintStream보다 PrintWriter사용을 권장

문자 기반 스트림

  • Reader, Writer : 문자기반 스트림의 최고 조상
  • FileReader/Writer : 문자 기반의 파일 입출력, 텍스트 파일의 입출력에 사용
  • PipedReader/Writer : 프로세스(스레드) 간의 통신에 사용하는 입출력
    • PipedReader::connect, PipedWriter::connect 중 하나로 입출력 연결 필요
  • StringReader/Writer : 메모리에 입출력. StringWriter에서 출력 데이터는 StringBuffer에 저장

문자 기반 보조 스트림

  • BufferedReader/Writer : 입출력 효율을 높이기 위해 버퍼를 사용하는 보조 스트림
    • 라인 단위 입출력이 장점
  • InputStreamReader/Writer : 바이트 기반 스트림을 문자 기반 스트림처럼 쓸 수 있게 해주는 스트림

표준 입출력

콘솔을 통한 데이터의 입출력을 말하며, JVM이 시작하면서 자동적으로 생성되는 스트림

  • System.in : InputStream
  • System.out : PrintStream
  • System.err : PrintStream

setIn, setOut, setErr 메서드로 표준 입출력 스트림을 변경할 수 있다.

직렬화에서 제외

  • java.io.Serializable을 구현하면 직렬 화가 가능하다
  • 직렬화에 제외하기 위해선 두 가지 방법이 존재한다.
    • 직렬화 하고 싶지 않은 클래스의 인스턴스에 대해 Serializable을 구현하지 않도록 한다.
    • 제어자 transient을 붙여 직렬화 대상에서 제외한다.
      • 정확히는 transient가 붙은 타입의 기본값으로 직렬화된다.

IP 주소

  • IP주소는 네트워크 주소와 호스트 주소로 되어있다.
    • IP가 a.b.c.d라면 a.b.c가 네트워크 주소, d가 호스트 주소이다.
  • 네트워크 주소가 같은 두 호스트는 같은 네트워크에 존재한다.
  • IP주소와 서브넷 마스크를 '&'연산하면 네트워크 주소를 얻는다.
반응형