안녕하세요 열코입니다.

이번시간에는 자바의 가비지 컬렉션에 대해 알아보겠습니다.


우리가 C/C++ 프로그래밍을 할 때 메모리 누수(Memory Leak)를 막기 위해 객체를 생성한 후 

사용하지 않는 객체를 파괴(메모리 해제)를 프로그래머가 직접 해주어야 했습니다.


하지만 Java에서는 JVM(Java Virtual Machine)이 구성된 JRE(Java Runtime Environment)가 

제공되며 JVM 구성 요소 중 하나인 가비지 컬렉션(Garbage Collection)이 자동으로 

사용하지 않는 객체를 파괴합니다.


JVM의 가비지 컬렉션은 다음과 같은 단계로 메모리 해제 작업을 수행합니다.

1. 마킹 작업 : 사용중인 메모리와 사용 하지않는 메모리를 식별합니다.

2. 일반 삭제 : 참조되지 않는 객체를 제거하고 빈 공간에 대한 포인터를 남겨둡니다.

+ 압축 삭제 : 삭제 된 객체 외 나머지 객체를 메모리 공간을 효율적으로 사용하며 삭제


또한 JVM의 가비지 컬렉션은 객체들을 세대로 나누어 처리합니다.

- 젊은 세대 : 새로운 모든 객체를 할당하는 곳입니다. 젊은 세대가 가득차면 가비지 컬렉션을 수행하며 

매우 빠른 속도로 진행됩니다. 사소한(가벼운) 가비지 컬렉션이 수행됩니다.

- 이전 세대 : 오랫동안 생존한 객체를 저장합니다. 젊은 세대에서 생성된 객체에 임계 값을 주어지고 어느정도

연령에 도달하면 이전 세대로 이동합니다. 이전 세대의 객체를 수집하는 것을 주요 가비지 컬렉션이라고 합니다.


세대 별 가비지 컬렉션은 다음과 같은 과정으로 수행됩니다.

1. 새로운 객체는 에덴(Eden) 공간에 할당되며, 그 외 두 개의 생존 공간이 모두 비어있는 상태입니다.

2. 에덴 공간이 가득차게 되면 사소한(가벼운) 가비지 컬렉션이 시작됩니다.

3. 참조된 객체는 첫번째 생존 공간으로 이동되며 참조되지 않는 객체는 모두 삭제됩니다.

4. 가비지 컬렉션이 수행되는 동안 첫번째 생존 공간의 객체들은 특정 임계값이 넘어가면 두번째 생존 공간으로 이동합니다.

5. 두 번째 생존 공간의 객체들도 특정 임계값이 넘어가면 이전 세대로 넘어가게 됩니다.

6. 이전 세대의 객체가 일정 크기 이상 수집되면 주요 가비지 컬렉션이 수행됩니다.


또한 다음과 같이 JVM에게 가비지 컬렉션을 실행하도록 요청할 수 있습니다.

System.gc() 메소드 또는 Runtime.getRuntime().gc() 메소드 사용 (수행되는 가비지 컬렉션은 동일합니다.)



아래는 객체를 가비지 컬렉션의 대상으로 만드는 방법입니다.

1. 메소드 내부에서 생성된 객체

class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


static void show() {

GCTest gct1 = new GCTest("gct1");

show2();

}

static void show2() {

GCTest gct2 = new GCTest("gct2");

}


public static void main(String[] args) {

show();

System.gc(); //  gct1과 gct2는 가비지 컬렉션의 대상이 됨.

}

}

위 코드와 같이 메소드가 호출된 후 내부에 일부 객체가 만들어지고 메소드가 종료됬을 때, 메소드 내부의 객체들은

익명이 되어 가비지 컬렉션의 대상이 됩니다.


2. 참조 변수 재 지정

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

GCTest gct1 = new GCTest("gct1");

GCTest gct2 = new GCTest("gct2");


gct1 = gct2;


System.gc();

}

}

위 코드는 한 객체의 참조가 다른 객체를 참조할 때 이전 객체는 더 이상 참조를 갖지 못해 가비지 컬렉션의 대상이 됩니다.


3. 참조 변수 무효화

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

GCTest gct1 = new GCTest("gct1");


gct1 = null;


System.gc();

}

}

위 코드는 객체의 참조 변수가 null로 변경되었기 때문에 가비지 컬렉션의 대상이 됩니다.



4. 익명 객체

Class GCTest {

String name;

public GCTest(String name) {

this.name = name;

}


public static void main(String[] args) {

new GCTest("gct1");


System.gc();

}

}

익명 객체의 참조는 어느곳에도 저장되지 않기 때문에 가비지 컬렉션의 대상이 됩니다.



이상 '자바 가비지 컬렉션'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.





안녕하세요 열코입니다.


이번에는 자바에서 크기가 동적인 배열을 사용할 때 주로 사용하는 

두가지 클래스 벡터(Vector)와 어레이리스트(ArrayList)에 대해 

비교분석 해보겠습니다.


먼저 벡터에 대한 설명입니다.


☞ Vector란?

- 벡터 클래스는 예전의 자바에서 제공했던 레거시 클래스입니다.

- 레거시 클래스란 Collections 프레임워크가 포함되어 있지 않던 초기 자바 버전에서 정의한 인터페이스입니다.

- 현재는 재구성 및 설계되어서 현재의 Collections 프레임워크와 완벽하게 호환됩니다.


 Vector의 특징

- 필요에 따라 크기를 동적으로 조절할 수 있는 동적배열을 구현합니다.

- 배열과 마찬가지로 정수 인덱스를 이용하여 배열에 액세스 할 수 있습니다.

- 동기화(Thread Safe) 되어있으며 한번에 하나의 스레드만 벡터의 메소드를 호출 할 수 있습니다.


※ 벡터에 대한 사용법은 추후에 포스팅하겠습니다.



다음 어레이리스트에 대한 설명입니다.


☞ ArrayList란?

- Collections 프레임워크의 일부이며 java.uitl 패키지 내에 존재합니다.

- 벡터와 마찬가지로 동적 배열을 사용하기 위해 사용됩니다.


☞ ArrayList의 특징

- 자바 표준 배열보다 약간 느릴수 있지만 배열에서 많은 조작이 필요로할때 유용하게 사용됩니다.

- 기본 데이터 타입(int, char 등)에 대해 만들수 없기때문에 Integer, Object 등의 객체에 대해 참조해서 사용합니다.


※ 어레이리스트에 대한 사용법은 추후에 포스팅하겠습니다.



이렇게 비슷한 두 클래스 벡터와 어레이리스트의 차이점은 무엇이며 어떤 상황에서 어떤 클래스를 사용 야 하는

지에 대해 알아보겠습니다.

실제로 두 클래스는 많은 차이점이 있지만 이 포스트에서는 주요 차이점에 대해서만 언급하겠습니다.


ArrayList와 Vector의 주요 차이점


1. 동기화(Synchronize)

Vector가 동기화 된다면 ArrayList는 동기화가 되지않은 상태입니다.

쉽게말해 Vector는 한번에 하나의 스레드만 엑세스(접근) 가능하며, ArrayList는 동시에 여러 스레드가 

작업할 수 있습니다.

ArrayList에서 여러 스레드가 동시에 엑세스하는 경우 개발자가 명시적으로 동기화하는 코드를 추가해야합니다.



2. 스레드 안전(Thread Safe)

스레드 안전이란 멀티 스레드 프로그래밍에서 여러 스레드가 동시에 접근이 이루어져도 프로그램 실행에 

문제가 없음을 뜻합니다.

앞서 말했듯이 Vector는 동기화 되어있기 때문에 한번에 하나의 스레드만 접근할 수 있기때문에 스레드 

안전합니다.

ArrayList는 동기화되지 않았기 때문에 명시적으로 동기화 할 필요가 있습니다.

 

3. 성능

ArrayList는 동기화 되지않았기 때문에 동기화 된 벡터보다 더 빠릅니다.


4. 크기 증가

Vector와 ArrayList 모두 동적 배열 클래스로 최대 인덱스를 초과할 때 추가되는 인덱스 수가 다릅니다.

Vector는 현재 배열의 크기의 100%가 증가하며, ArrayList의 경우 현재 배열의 크기의 50%가 증가합니다.



★ 결론

멀티스레드 환경이 아닌 경우 ArrayList를 사용하는것이 바람직합니다.

Vector를 사용하기 위한 명시적 요구 사항이 없는경우 ArrayList를 사용하도록 합시다.



이상 '자바 벡터와 어레이리스트의 비교'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.





자바에서 NullPointerException은 RuntimeException입니다. 특수한 널 값은 객체 참조에 할당할 수 있습니다. 

프로그램에 널값을 가지는 객체 참조를 사용하려고하면 NullPointerException이 throw됩니다.


null에 대해

- null이란 아무것도 없음을 의미합니다.(0또는 공백 : "")

- 모든 참조유형에 대한 기본 값은 null입니다.

- null은 유효한 객체 인스턴스가 아니므로 할당 되는 메모리가 없습니다.


+ 예외가 발생하는 경우

- null 객체에서 method를 호출하는 경우

- null 객체의 필드에 접근하거나 값을 변경하는 경우

- null 의 길이를 배열처럼 취하는 경우

- null 을 throw 하는 경우

- null 을 통해 동기화 할 경우


+ 그럼에도 null 값이 필요한 이유? 

- 널은 자바에서 사용되는 특수 값입니다. 주로 참조 변수의 값이 할당되지 않았음을 나타내는데 사용됩니다.

- Null Object 패턴(로그 시스템) 및 Singleton 패턴에 사용됩니다.

- 연결된 목록 및 트리와 같은 데이터 구조를 구현하는 데 사용됩니다.


+ NullPointerException을 피하는 방법

1. 문자열 비교 : String 변수와 리티럴문자를 비교할 때 다음과 같은 오류가 발생할 수 있습니다.


String ptr = null;

if (ptr.equal("abc") // NullPointerException 발생!

{

System.out.println("SAME!");

}

else

{

System.out.println("NOT SAME!");

}


이는 다음과 같이 null객체 대신 리터럴문자에서 equal 메소드를 호출함으로써 해결할 수 있습니다.


String ptr = null;

if ("abc".equals(ptr))

{

System.out.println("SAME!");

}

else

{

System.out.println("NOT SAME!"); // "NOT SAME" 출력

}


2. try - catch문 사용 : 다음과 같이 예외처리문을 사용가능합니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

try

System.out.println(getLength(s)); 

catch(IllegalArgumentException e) 

System.out.println("IllegalArgumentException caught"); // 예외처리 발생!!

}

public static int getLength(String s) 

if (s == null) 

{

throw new IllegalArgumentException("The argument cannot be null");

}

return s.length(); 

}


3. 삼항 연산자 사용 : null 값은 == 또는 != 의 연산자가 적용되기 때문이 이를 이용하여 다음과 같이 코드를 작성할 수 있습니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

System.out.println(s.length()); // NullPointerException 발생!!

        

}


위 코드를 아래와 같이 수정합니다.


public class Main {

public static void main(String[] arg) {

String s = null; 

System.out.println((s == null) ? "0" : s.length()); // "0" 출력

        

}


이상 java.lang.NullPointerException에 대해 알아보았습니다.

자바 예외에 대해 더 알고싶은 분은 여기를 참고하세요.

또한 본 게시글에 대한 오류나 질문사항은 아래 댓글로 남겨주세요.

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.



자바는 배열의 작성 및 조작을 데이터 구조로 지원합니다. 

배열의 크기를 n이라고 했을 때 배열의 인덱스는 1부터 n까지가 아닌 0부터 n-1까지입니다. 

프로그래밍 중 ArrayIndexOutOfBoundsException이 가장 많이 발생하는 이유중 하나죠.

C/C++과 달리 자바는 인덱스가 배열의 크기보다 크거나 음수 인덱스에 대한 요청이 있으면 자바는 위의 예외를 발생시킵니다.

또한 이 예외는 자바 컴파일러는 검사하지않고 항상 런타임(실행도중)에 예외를 발생시킵니다.


다음은 Exception 발생의 예입니다.


int[] arr = {0, 1, 2, 3, 4};

arr[5] = 4; // 예외 발생!!


해당 프로그램 실행 시 다음과 같은 오류가 발생합니다.


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 

at average.Main.main(Main.java:9)


arr이란 배열의 크기는 5입니다. 따라서 인덱스는 0부터 4(n-1)까지입니다.

하지만 배열의 5번째 인덱스에 접근하려고 시도했기 때문에 자바는 해당 예외를 throw한 것입니다.


또 다른 Exception 발생의 예입니다.


ArrayList<String> list = new ArrayList<>();

list.add("Hello");

list.add("Wolrd");

list.get(2); // 예외 발생!!


해당 프로그램 실행 시 다음과 같은 오류가 발생합니다.


Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 2, Size: 2 

at java.util.ArrayList.rangeCheck(ArrayList.java:657)

at java.util.ArrayList.get(ArrayList.java:433)

at average.Main.main(Main.java:11)


위 코드를 보시면 list 객체를 생성하고 2번 add해주었습니다. (list의 크기 = 2)

list의 크기가 2이므로 접근 가능한 인덱스는 0부터 1(2-1)까지입니다. 따라서 list의 2번째 인덱스에 접근하려고 했기 때문에 해당 예외를 throw한 것입니다.

그렇다면 프로그래머는 어떤식으로 이런 예외에 대해 올바르게 예외처리를 수행할 수 있을까요?


☞ 배열에 올바르게 접근하는 방법


반복문에서 다음과 같이 사용합니다.


for(int i = 0; i < arr.length; i++) { ... }


또한 foreach문을 사용할 수 있습니다. (foreach문에 대한 설명)


for(int i : arr) { ... }


Try-Catch문 사용하기 : 다음과 같이 사용 가능합니다.


import java.util.ArrayList;


public class Main {

public static void main(String[] arg) {

try {

ArrayList<String> list = new ArrayList<>();

list.add("Hello");

list.add("Wolrd");

list.get(2);

}

catch(IndexOutOfBoundsException e) {

System.out.println(e);

}

}

}


이상 java.lang.ArrayIndexOutOfBoundsException에 대해 알아보았습니다.

자바 예외에 대해 더 알고싶은 분은 여기를 참고하세요.

또한 본 게시글에 대한 오류나 질문사항은 아래 댓글로 남겨주세요.

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.


자바 foreach문 사용법



foreach(For-each)문은 for, while, do-while 반복문과 같은 배열 탐색 기법입니다.


사용방법은 다음과 같습니다.


☞ 일반적인 for 반복문과 동일하게 for 키워드를 사용합니다.

☞ 반복문 내에 카운터 변수를 선언하고 콜론(:) 다음 배열이름을 순서대로 선언합니다.

☞ 일반적으로 배열이나 Collection 클래스(ArrayList ... 등)를 반복하는 데 사용됩니다.


※ 구문(Syntax)

일반적으로 배열을 탐색할 때 다음과 같이 for 반복문을 사용합니다.

int[] arr = {0, 1, 2, 3, 4};

for (int i = 0; i < 5; i++) { 

System.out.println(arr[i]); // 0 1 2 3 4 출력

}


위 반복문을 foreach 반복문으로 다음과 같이 표현할 수 있습니다.

int[] arr = {0, 1, 2, 3, 4};


for (int i : arr) { 

System.out.println(arr[i]); // 0 1 2 3 4 출력

}


이러한 foreach 반복문을 사용함으로써 가변하는 복잡한 배열이나 리스트의 크기를 일일이 구할 필요가 없습니다. 이중 for문이나 복잡한 반복문에 적합하며, 인덱스를 생성해 접근하는 단순 for문 보다 수행속도가 조금 더 빠릅니다.

무엇보다도 코드가 짧아서 좋습니다 ㅎㅎ(가독성도 높구요)


이러한 사용하기도 편하고 성능 빵빵한 foreach문에게도 한계점이 존재하는데요...


※ foreach 문의 한계

배열 및 리스트를 탐색하는 반복문을 구현하기 편한 foreach 반복문에게도 한계가 존재합니다.


⊙ 반복문 내에서 배열이나 리스트의 값을 변경 하거나 추가할 수 없습니다.

for(int i : arr) {

arr[i] = 3; // 오류 발생!!, i가 무엇?

arr[i+1] = 4; // 오류 발생!! i가 대체 무엇???

}


⊙ 배열을 역순으로 탐색할 수 없습니다.

for(int i : arr) { // 표현 조차 불가능 ㅜㅜ

...

}


이러한 장, 단점을 가진 foreach문을 적절히 사용하여 가독성 높고 성능 좋은 프로그래밍을 하시기 바랍니다. 혹시 오타가 있는 부분이나 궁금한 점이 있는분은 댓글로 남겨주시기 바랍니다. 

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.



to Top