devlog of ShinJe Kim

[TWIL] 2020-01-17 (금)

|

자바에서 사용되는 변수

자바의 정석을 보면 변수의 선언 위치에 따라 클래스 변수, 인스턴스 변수, 지역 변수의 세 가지 종류가 있다고 설명한다.(p.246)

  • 클래스변수는 클래스가 메모리에 올라갔을 때 생성된다.
  • 인스턴스변수는 인스턴스가 생성되었을 때 생성된다.
  • 지역변수는 변수 선언문이 수행되었을 때 생성된다.

이외에 멤버 변수는 빈(Bean) 클래스에서 해당 클래스의 속성을 정의하는 변수를 의미한다. 또한 참조변수는 참조 타입을 가져 값이 아닌 주소를 가리키는 변수를 의미한다.

final 제어자(modifier)

final은 클래스, 메서드, 멤버변수, 지역변수 에 사용될 수 있다.

  • final 클래스: 변경되거나 확장될 수 없는 클래스를 의미한다. 따라서 final 클래스는 상속이 불가능하다.
  • final 메서드: 변경될 수 없는 메소드를 의미한다. 오버라이딩이 불가하다.
  • final 멤버변수, 지역변수: 값을 변경할 수 없는 상수가 된다.

static 제어자(modifier)

static은 멤버변수, 메서드, 초기화 블럭 에 사용될 수 있다.

  • static 멤버변수: 클래스가 메모리에 로드될 때 단 한 번만 수행되어 생성된다. 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 되며 클래스 변수는 인스턴스를 생성하지 않고도 사용이 가능하다.
  • static 메서드: 인스턴스를 생성하지 않고도 호출이 가능하며, static 메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다.(static 메소드에서는 static 멤버변수나 메소드만 호출이 가능하다)

생성자(constructor)

생성자는 객체의 데이터를 초기화하는 방법을 제공해주는 메서드이다. 객체의 멤버 변수에 값을 지정해준다는 점에서 setter와 비슷하지만, 생성자는 인스턴스 생성과 동시에 필드값을 초기화하고 setter 메소드는 인스턴스 생성 후 필드값을 넣어준다. 생성자의 이름은 반드시 클래스 이름과 같아야 한다.

생성자는 JVM이 자동 호출해주며, 사용자가 직접 호출 순서를 제어할 수도 있다. 클래스 내부에 생성자가 하나도 없으면 내부적으로 자동으로 기본생성자(default constructor)가 생성된다. 이 떄 주의할 점은 컴파일러가 자동적으로 기본 생성자를 추가해주는 경우는 ‘클래스 내에 생성자가 하나도 없을 때’뿐 이라는 것이다.

class B extends A {
    //위의 의미는 아래의 코드를 내포하고 있음
    B() { A(); }
}

위의 구조에서 B객체를 생성하면 어떻게 될까? 부모 클래스인 A의 멤버를 먼저 메모리에 올린 뒤, B의 멤버를 메모리에 올린다. 그래서 A의 멤버는 가려지게 됨.

  • 추상 메서드 (부모 메서드의 이름만 정의된 메서드를 의미함)의 목적?
  • 상속의 필요성
    • 코드의 중복을 줄임
    • 프로젝트 내의 클래스 구조를 효율적으로 관리할 수 있음
    • 코딩의 일관성

더 찾아볼 것

  • 컴파일러는 왜 기본 생성자를 자동으로 생성해줄까? 기본생성자(default constructor)의 의미와 필요성
  • Bean 클래스의 정의 => 필요한 데이터를 저장할 멤버 변수들과 게터와 세터 함수만 존재하는 클래스?
  • getter & setter의 정의 => 다른 클래스에서 접근할 때 사용되는 함수?
  • 자바의 클래스는 확장 개념이 아닌 상속 개념이라고 한다.(그럼 확장 개념을 가진 언어는 뭐가 있을까? 객체지향 언어가 아닌걸까?)
  • 상속이 필요한 이유?
  • 상속의 단점?(이펙티브 자바 아이템 18, 19)
  • 인터페이스에서 상수만 사용가능한 이유는 무엇인가? -> 통일성을 위해서…???
  • 그냥 추상클래스를 다중상속 가능하게 하면 되는데 굳이 추상클래스와 인터페이스의 용도를 나눈 이유는 무엇인가? 각각의 필요성이 뭘까?(정의(definition) 말고 필요한 이유..!)
  • 객체지향프로그래밍이란?
    • SOLID
    • 다형성
    • 상속
    • 추상화
    • 정보은닉

[Java] 자바의 메모리 구조

|

자바의 메모리 구조

자바(JVM)의 메모리 영역은 heap, stack, static이 전부인것으로 알고있었다. 그런데 구글링을 해보니 블로그마다 설명하는 용어가 조금씩 달랐고 찾으면 찾을수록 처음 들어보는 메모리영역에 대한 설명이 나왔다. 궁극적으로 모두를 포함하는 설명을 찾았는데 이 글에서는 자바의 메모리 영역을 아래처럼 힙 메모리(Heap memory), 비(非) 힙 메모리(Non-Heap memory), 기타의 세 가지로 나누어 설명한다. 한국어로는 힙 ‘영역’이라고 명명하는 것 같아 아래에서는 ‘영역’이라고 표기했다.

JVM-memory-structure1.png

힙 영역(Heap memory)

힙 영역은 모든 자바 클래스의 인스턴스(instance)와 배열(array)이 할당되는 곳으로, 런타임(run time) 데이터를 저장하는 영역이다. 힙 영역은 JVM이 시작될 때 생성되어 애플리케이션이 실행되는 동안 크기가 커졌다 작아졌다 한다. 힙 영역의 크기는 -Xms VM option으로 지정된다고 한다. 힙 영역의 크기는 가비지 컬렉션의 전략에 따라 고정된 크기일수도 있고, 유동적으로 변경될 수도 있다. 힙 영역의 최대 크기는 -Xmx option으로 설정되는데, 디폴트로 설정된 힙 영역의 크기는 64MB이다.

힙 영역의 구조(Java(JVM) Heap Memory Structure)

JVM-memory-structure2.png

JVM의 힙 영역은 물리적으로 두 부분(two parts 혹은 two generation)으로 나눠진다: nursery(혹은 young space/young generation) 파트와 old space(혹은 old generation)이다.

nursery는 새로운 객체를 할당하기 위해 힙에 확보된 공간이다. nursery가 가득 차면 young collection이라는 것을 실행하여 쓰레기(garbage)를 수집한다. 이 young collection은 nursery에 어느정도 오래 머문 모든 객체들을 old space로 이동시켜 nursery가 더 많은 객체를 할당할 수 있도록 해준다. 이러한 가비지 컬렉션(garbage collection)을 Minor GC라고 한다. nursery는 세 가지 부분으로 나뉜다 - Eden Momory와 두 개의 Survivor Memory 공간(spaces)이다.

비 힙 영역(Non-Heap memory)

비 힙 영역이라는 말이 어색하므로 Non-Heap 영역이라고 표기하겠다. Non-Heap 영역은 힙 영역과 마찬가지로 JVM이 시작할 때 생성된다. 이 영역에는 런타임 상수 풀, 필드 및 메소드 데이터와 같은 클래스 별 구조와 메소드 및 생성자에 대한 코드뿐만 아니라 내부 문자열이 저장된다. 디폴트로 지정된 Non-Heap 영역의 크기는 64MB이며, 이는 XX:MaxPermSize VM option을 통해 변경될 수 있다.

다른 메모리 영역(Other memory)

이 영역은 JVM 자체의 코드와 JVM의 내부 구조, 로드된 프로파일러 에이전트 코드(loaded profiler agent code)와 데이터 등을 저장하기 위해 사용된다.

String이 메모리에 저장되는 방법

자바에서 String을 생성하는 데에는 두 가지 방식이 있다. 하나는 new 연산자를 이용하는 방식이고 다른 하나는 리터럴을 이용하는 방식이다.

리터럴(literal)이란?

컴퓨터 과학 분야에서 리터럴(literal)이란 소스 코드의 고정된 값을 대표하는 용어다.(위키피디아)

예를 들어 String a = "apple";이라고 선언해보자. a라는 변수에 apple이라는 값이 할당될 것이다. 이 때 변수 a는 메모리에 할당된 공간이다. 그리고 applea가 할당된 공간에 저장되는 값이다. 이 값 자체를 리터럴이라고 한다.

두 가지 선언 방식의 차이점을 표로 정리해보았다.

  new 선언 리터럴 선언
저장 장소 Heap영역에 저장 String constant pool에 저장
동작 방식 ?? String constant pool에 문자열이 존재하면 해당 주소값을 반환, 존재하지 않는다면 새로 저장하고 새로운 주소값을 반환
동일한 문자열 비교시 .equals로 비교해야 같은 결과 ==으로 비교 가능

동일한 문자열을 위의 두 가지 방식으로 생성하여 == 연산자와 .equals()로 비교해보면 아래와 같은 결과가 나온다.

public static void main(String[] args) {
    String a = "apple";
    String b = "apple";
    String c = new String("apple");
    
    System.out.println(a == b); // true
    System.out.println(a == c); // false
    
    System.out.println(a.equals(b)); // true
    System.out.println(a.equals(c)); // true
}

Wrapper 클래스와 박싱(Boxing)

래퍼(Wrapper) 클래스의 존재 이유가 무엇일까? 원시 타입(primitive type)의 변수를 객체로 다뤄야 하는 경우가 있기 때문이다. 아래가 대표적인 경우이다.

  • 매개변수로 객체가 요구될 때(예. 제네릭)
  • 원시타입이 아닌 객체로 저장해야 할 때
  • 객체간의 비교가 필요할 때

이 때 원시 타입을 Wrapper 클래스로 변경해주는 것을 박싱(Boxing)이라고 하며, 반대의 경우를 언박싱(Unboxing)이라고 한다. 두 가지 타입을 자동으로 상호 변환해주는 것을 오토박싱(AutoBoxing)이라고 하는데, 자바에서는 오토박싱을 지원한다. 아래의 예시를 통해 살펴보겠다.

public static void main(String[] args) {
    int a = 3;
    Integer b = 5;

    // 아래 두줄 모두 에러가 나지 않는다! 
    int c = b; 
    Integer d = a;
}

int c = b; 코드와 Integer d = a; 코드는 대입값과 다른 타입의 변수에 값을 대입하고 있지만 에러가 발생하지 않는다. 그 이유는 자바에서 오토박싱을 해주었기 때문이다.

참고 자료

[Java] 자바 환경변수 설정이 필요한 이유?

|

자바 환경변수 설정이 필요한 이유?

구글에 검색해보면 자바 환경변수를 설정해야 하는 방법을 찾아 따라하면 누구나 설정할 수 있다. 따라서 이 부분은 생략하고 환경변수를 왜 설정해야하는지 그리고 환경변수를 설정할 때 나오는 개념들에 대해 정리해보겠다.

운영체제란 하드웨어를 사용할 수 있게끔 해주는 프로그램이다. 운영체제가 없으면 하드웨어 위에 어플리케이션을 올릴 수 없다. (운영체제가 아닌 프로그램은 모두 어플리케이션이라고 한다.) 이 때 운영체제가 컴퓨터의 어떤 경로에서든 특정 파일(파일의 형식으로 되어있는 어플리케이션)을 인식할 수 있도록 환경변수를 등록하는 것이다.(환경변수를 설정하지 않았을 때 cmd 창에서 java를 사용할 수 없었던 경험은 한 번쯤 있을 것이다.)

환경변수란?

위키백과에 따르면 환경변수란 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는, 동적인 값들의 모임이다. 인터넷에서 조금더 찾아봤더니 ‘운영체제가 참조하는 변수’라는 정의가 있다.

환경변수를 설정할 때 jdk의 경로가 필요하다. 왜일까? 자바를 다운로드 받아 디렉토리로 들어가보면 jdk와 jre라는 두 가지 경로가 있다. jdk는 개발과 실행이 동시에 가능한 디렉토리이고, jre는 실행만 가능한 디렉토리다. 우리는 개발을 할 것이기 때문에 jdk의 경로를 환경변수로 설정해주어야 하는 것이다.

그런데 한 가지 더 궁금한 것이 있다. 환경변수를 설정할 때 jdk의 bin 디렉토리 경로까지 넣는 이유는 무엇일까? 그 이유는 bin에 실행파일이 있기 때문이다. 그렇다면 실행 파일은 또 무엇인가? 특정 프로그램을 실행하려면 아주 많은 파일이 필요하다. 하지만 프로그램이 실행되도록 해주는 프로그램은 단 하나이다. 자바에서는 bin에 그 실행 파일이 있기 때문에 환경변수를 설정할 때 bin경로까지 설정하는 것이다. 실행 파일은 보통 .exe, .com, .bat 확장자로 끝난다고 한다.

환경변수를 설정하고 나면 우리는 javac라는 명령어를 명령 프롬프트에 입력하여 설정이 제대로 되었는지 확인한다. 그런데 java라는 명령어도 써본적이 있지 않은가? javacjava의 차이점은 무엇일까?

javac.java 파일을 .class 파일로 변환시켜주는 컴파일러를 의미한다. 그래서 Hello.java파일이 있다는 가정하에 javac Hello.java라는 명령어를 입력하면 Hello.class라는 파일이 생성될 것이다. 이와 다르게 javajavac로 컴파일된 .class 파일을 실행하는 명령어이다. 그래서 java Hello.java라는 명령어를 입력하면 Hello.java파일 안의 내용이 실행된다.

[Java] Java란 어떤 언어인가?

|

Java 언어의 특징

  1. 운영체제에 독립적이다(WORA).

    자바로 작성된 소스코드는 .java라는 확장자를 가지는 파일이다. .java 파일이 컴파일되면 .class라는 확장자를 가진 바이트 코드 파일이 생성되며, 이 .class파일은 JVM(Java Virtual Machine)이라는 자바 가상 머신을 통해 운영체제에서 실행된다. 이 JVM의 존재 덕분에 자바는 단 한 번만 컴파일되면, JVM에 의해 각 운영체제에 맞게 변환되어 실행된다. 이를 WORA(Write Once Run Anywhere), 즉 한 번 작성하면 어디서나 실행된다고 한다.

  2. 객체지향언어이다.

    자바는 객체지향 프로그래밍(OOP; Object Oriented Programming)언어이며, 객체지향개념의 특징인 상속, 캡슐화, 다형성 등을 작 적용한 언어이다.

  3. 가비지 컬렉션(Garbage Collection)

    자바의 가비지컬렉터(garbage collector)라는 것이 메모리관리를 자동으로 해준다. 그래서 프로그래머가 일일이 코드로 메모리관리를 해주지 않아도 된다.

    메모리 관리를 직접 하는지 혹은 gc(가비지컬렉터) 같은 존재가 자동으로 관리해주는지에는 각각의 장단점이 있다. 메모리 관리를 직접 하면 어디서 메모리가 반환되고 그렇지 않은지 명확하게 코드로 볼 수 있지만 번거롭고 보일러플레이트 코드가 생성된다는 단점이 있다. 반면 gc가 자동으로 메모리관리를 해주면 보일러플레이트 코드도 줄어들고 덜 번거롭겠지만 메모리가 반환되는 지점이 명확하게 보이지 않는다.

    메모리란?

    기억 장치(記憶裝置)는 컴퓨터에서 자료를 일시적으로, 또는 영구히 보존하는 장치를 말한다. 비슷한 말로 저장 장치라고도 하는데 이때는 대체로 비휘발성의 기억 장치를 의미한다. 컴퓨터의 기억 장치는 주기억 장치와 보조 기억장치로 나눌 수 있다. 메모리(memory)는 종종 ‘기억 장치’라는 용어와 혼용되기도 하지만, 대체로는 주기억장치를 말하며 특히 램을 가리키는 경우가 많다.(위키피디아)

  4. 멀티스레드를 지원한다.

    스레드(Thread)란?

    스레드를 알기 위해서는 프로세스(process)를 먼저 알아야 한다. 프로세스는 운영체제에서 메모리 공간을 할당받아 현재 실행중인 프로그램을 의미한다. 프로세는 프로그램에 사용되는 데이터, 메모리 등의 자원, 그리고 스레드로 구성된다. 스레드는 프로세스 내에서 실제로 작업을 수행하는 주체이다. 모든 프로세스는 한 개 이상의 스레드가 존재하여 작업을 수행하는데, 두 개 이상의 스레드를 가지는 프로세스를 멀티 스레드 프로세스라고 한다.

    멀티스레드를 구현하면 동시에 여러 작업을 하거나 대용량의 작업을 더 빨리 처리할 수 있다. 자바에서는 이러한 멀티스레드 프로그래밍을 지원한다. 일반적으로 멀티스레드는 운영체제에 따라 구현 방법과 처리 방식이 다른데, 자바에서는 시스템과 관계없이 구현이 가능하며 관련 라이브러리인 자바 API가 제공되기때문에 구현이 상대적으로 쉽다. 또한 여러 스레드에 대한 스케줄링(scheduling)을 자바 인터프리터가 담당하게 된다.

    스케줄링(scheduling)이란?

    CPU는 한 번에 한 가지 작업만 가능하다. 따라서 여러개의 작업을 동시에 수행하는 것처럼 보이게 하기 위해서는

  5. 동적 로딩(Dynamic Loading)을 지원한다.

    동적 로딩은 프로그램 실행 시에 모든 클래스가 한 번에 로딩하여 객체를 생성하는 것이 아니라, 필요한 시점에 클래스를 로딩하여 객체를 생성하는 것을 의미한다. 동적 로딩을 하면 유지보수시 특정 객체만 쉽게 수정하고 교체할 수 있으며, 일부 클래스가 변경되어도 전체 애플리케이션을 다시 컴파일하지 않아도 된다는 장점이 있다.

Java의 동작 원리

자바로 작성된 애플리케이션은 모두 JVM(Java Virtual Machine)이라는 자바 가상머신에서만 실행되기 때문에 자바 애플리케이션이 실행되기 위해서는 반드시 JVM이 필요하다. 일반 애플리케이션의 코드는 OS만 거치고 하드웨어로 전달된다. 반면 Java 애플리케이션은 JVM을 한 번 더 거친다. JVM을 거치더라도 하드웨어에 맞게 완전히 컴파일 된 상태가 아니기 때문에 실행시에 해석(interpret)이 필요하여 속도가 느리다는 단점이 있다. 하지만 요즘은 바이트코드(컴파일된 자바 코드)를 하드웨어의 기계어로 바로 변환해주는 JIT 컴파일러와 향상된 최적화 기술이 적용되어 속도의 격차가 많이 줄어들었다(자바의 정석, p.5).

기타

  • new Instance를 호출하면 String 클래스를 제외한 모든 클래스는 Object 객체를 지원한다. String 클래스만 유일하게 String 객체를 리턴한다.
  • 이스케이프 문자를 사용하기는 했지만 정확한 정의를 알지 못했다. 제타위키에 따르면 어떤 부호 또는 언어로부터 이탈하는 것을 의미한다고 한다. 조금 더 자세한 설명은 생활코딩에 있다.
  • C언어는 리눅스나 맥에서는 동작하지 않는다. 따라서 윈도우즈에서만 개발 가능하다고 한다.

참고 자료

[TIL] 2019-12-23 (월)

|

Today I Learned

CTO님이 개발 환경 구축에 대한 조언을 주셨다. 컴퓨터를 잘 하는 사람과 못하는 사람의 큰 차이중 하나는 ‘세팅’에 대한 차이라고 하셨다. CTO님은 컴퓨터가 여러대이신데 모든 컴퓨터의 환경과 워크스페이스 디렉토리를 동일하게 세팅해두어서 어디서 작업을 하더라도 차질없도록 해놓으신다고 했다. 실제로 보니 관리하는 작업물의 양이 엄청난데 워크스페이스가 너무 깔끔해서 깜짝 놀랐다.

생각해보니 내가 내 노트북으로 작업하기를 그만두고 회사 노트북을 들고다니기 시작한 것은 (물론 모니터 크기 차이와 성능 차이도 있지만) 작업 환경이 달라서 뭔가를 하려고 할 때마다 너무 거슬리고 번거로웠기 떄문이다. 미숙하고 서투를수록 나의 생산성을 높여주는 도구들의 도움을 받는 것이 더 중요한데, 나는 영리하게 생산성을 높이기보다는 주먹구구식으로 열심히만 (그게 더 답답하다 이사람아) 하려고 했던 것 같아 정신이 번쩍 들었다. 터미널이나 깃을 쓴다고 해도 늘 사용하는 명령어만 썼었고, 어떻게 하면 더 간편하고 효율적으로 쓸 수 있을지에 대한 고민은 늘 제쳐뒀었다.

CTO님이 알려주신 블로그 포스팅을 보고 iTerm, zsh, neovim, fzf 등등을 설치하였다. 사용법을 연습해보며 어떻게하면 반복 작업을 줄이고 더 효율적으로 일상적인 일들을 할 수 있을지 고민해봐야겠다. (일정 기간 사용해본 후에 과거에 내가 비효율적으로 작업했던 방식이랑 비교해보면 좋을 것 같다.)