-
JAVA 기본 - 에러 처리 구조, JAVA메모리 사용 관행개발언어/JAVA 2023. 12. 27. 11:48
에러 처리 구조
어떤 지점에서 발생할 수 있는 에러에 대해 장치를 해놓으면 디 버깅 시간을 절약할 수 있다. 즉, 에러 상황이 발생했을 때 적절하게 처리하면 프로그램을 종료하거나 계속 수행할 수 있다. 자바에는 에러가 발생할 때 미리 지정한 에러 객체를 생성해서 던지는 기능이 있다. throw기능이다.
Heap.java
public class Heap{ ... public Heap(int n){ item = new int[n]; size = 0; maxHeap =n; } 1️⃣class HeapException extends Exception{ // 에러 클래스 설계 public HeapException(String msg){ super(meg); } } 2️⃣public void insert(int newItem) throws HeapException { 3️⃣if(size == maxHeap){ throw new HeapException("Overflow in insert()"); }else{ /*정상 수행*/ } } public static void main(String[] args){ Heap h = new Heap(3); 4️⃣ try{ h.insert(1); h.insert(10); h.insert(30); h.insert(20); //에러내기 //앞에서 Exception 객체가 날아와서 //여기서부터는 수행하지 못하고 catch로 넘어간다. h.insert(100); ... 5️⃣ }catch(HeapException ex){ Sytem.out.println("HeapException "+ ex.getMessage()); /*추가 처리가 필요하면 여기에*/ } } }
메서드 insert()에서 에러를 던지려면 함수를 선언할 때 던질 에러 이름을 명시한다. 2️⃣에 throws HeapException이라고 명시했다. 3️⃣의 케이스가 발생하면 HeapException 객체를 생성해서 던진다. 그러면 기다리고 있던 5️⃣에서 HeapException 객체를 잡아챈다.
insert()를 사용하는 클라이언트에서 이 에러가 발생할 때를 대비해서 try-catch 구조로 처리한다. try 안에 수행할 코드를 넣고 수행하는 동안 HeapException 에러가 나면(그 안에 포함된 메서드가 HeapException 객체를 던지면) 수행을 중단한고 catch로 넘어간다. 5️⃣의 ex는 넘어온 HeapException 타입의 객에를 레러펀스하는 파라미터다. 필요하면 5️⃣의 catch 아랫 부분에서 에러 내용을 프린트하는 작업에 추가 처리 코드를 함께 적어주면 된다. Exception 타입 또는 하위 객체의 메서드 getMessage()는 파라미터로 넘어온 문자열을 리턴한다. 이 예제 코드에서는 "Overflow in insert()"이다.
try{ ... }catch(...){ ... }catch(...){ ... }finally{ /*임의의 catch 처리 후 마지막으로 처리할 일*/ }
try-catch를 더 세분화할 수도 있고, 문장마다 해당되는 에러 객체를 따로 설정할 수 있다. 던질 에러의 이름도 Exception의 하위 클래스로 다양하게 만들 수 있다. 이름이 Exception으로 끝나지 않아도 수행하는 데 문제는 없다. try-catch 구조에 위 코드 처럼 catch가 끝난 후 마지막 처리를 명시해 try-catch-finally 구조를 만들 수도 있다.
Exception클래스를 상속 받은 예외들
이름 설명 ArithmeticException 0으로 나누는 에러 발생 시 생성 NullPointerException null 객체에 접근하려고 할 때 생성 IndexOutOfBoundsException 배열 등의 인덱스가 범위를 벗어날 때 생성 자바의 모든 에러 객체는 클래스 Exception의 하위 객체다. 즉. Ecveption을 상속한다. 이 Exception의 하위 객체를 생성하기 위해 자바에서 미리 만드렁 놓은 클래스가 12개 있다.
Heap.java 코드에서 HeapException은 미리 준비되 클래스가 아니므로 1️⃣에서 새 클래스로 만들어 주었다. 자바의 모든 에러 객체는 최상위 에러 클래스인 Exception의 하위 클래스가 되어야 하므로 extends로 상송하였다. (참고로 클래스 Exception은 클래스 Throwable을 상속하고 있다. getMessage()는 클래스 Throwable에서 만들어 놓은 메서드다.)
Heap.java 코드 3️⃣의 에러를 체크하지 않고 수행하게 되면 이 프로그램은 어차피 else{}부분에서 배열의 범위를 넘는 접근을 시도하게 되고 자바는 IndexOutOfBoundsException 객체를 발생시킨다. 그러면 3️⃣에서 굳이 에러 객체를 발생시키지 않아도 5️⃣에서 HeapException 대신 IndexOutOfBoundsException을 사용해서 에러를 처리 할 수 있다.
그렇지만 이렇게 자바가 미리 준비해놓은 에러 객체를 이용하는 방식은 피하는 것이 좋다. 자바가 4️⃣의 try부분을 수행하는 중에 insert()가 아닌 다른 곳에서 IndexOutOfBoundsException객체를 발생 시킬 수도 있다. 이 경우 자바 시스템보다 IndexOutOfBoundsException 를 기다리고 있는 사용자의 catch 부분(5️⃣)이 우선권을 갖는다. 결과적으로 다른 곳에서 발생한 에러가 여기서 처리 되는 일이 발생한다.
비슷한 맥락으로 3️⃣의 HeapException을 그대로 두고 5️⃣에서 HeapException 대신 모든 에러 객체를 포괄하는 Exception을 사용해도 3️⃣에 던진 에러 객체를 받아 준다. 그렇지만 이것 역시 자바가 4️⃣의 try부분을 수행하는 중에 insert()가 아닌 다른 곳에서 임의의 에러 객체를 발생시키면 자바 시스템보다 5️⃣의 catch부분이 우선권을 가져 5️⃣에서 처리하길 원하지 않는 에러가 여기서 처리 되는 일이 발생한다.
메모리 사용 관행: 정적/동적 영역, 가비지 컬렉션
모든 고급 프로그램은 컴파일 시점에 메모리의 어느 위치에서 수행될지 알 수 없다. 그래서 일단 0번지에서 시작하는 가상 메모리를 가정하고 컴파일한 다음 수행 시 메모리의 실제 주소에 바인딩 된다. 그래서 한 프로그램이 수행되기 시작하면 특정 크기(N이라 하자)의 메모리를 할당받은 다음 그것이 메모리 0번 주소부터 N -1번까지인 것처럼 취급한다. 즉, 자신의 가상 공간에서 수행되는 것처럼 처리한다. 실제로는 메모리의 시작 주소를 더해 가상 주소가 수행된다.
자바에서는 JVM(자바가상머신)위 와 같은 기능을 한다. 즉. 가상 공간에서 프로그램을 수행시키기 위해 메모리를 기능에 따라 나누고 그 위해서 프로그램을 수행시킨다.
정적 공간은 프로그램 코드와 전역 변수, 정적 변수가 저장되는 곳이다. 변수 선언 시 예약어 static을 붙이면 정적 변수란 뜻이고, 정적 변수는 프로그램이 끝날 때까지 없어지지 않는다. 정적 공간은 컴파일이 끝나면 수행되기 전에 이미 주인이 결정된다. 프로그램 코드는 변하기 않고 수행이 끝날 때까지 있어야 하고, 전역 변수와 정적 변수도 끝날 땨까지 없어지지 않고 남아 있어야 하므로 '정적'이라 할 수 있다. 이처럼 끝날 때까지 보관해야 하는 대상들이 저장되는 공간을 정적 메모리(Static Memory)라 한다.
반면, 함수(또는 메서드)는 한시적으로 호출되므로 수행이 끝나면 공간을 잡고 있을 필요가 없다. 그래서 스택 자료구조를 이용해서 공간을 할당한 다음 호출되어 살아 있는 함수를 순서대로 쌓는다. 스택의 맨 위에 지금 수행 중인 함수가 자리하고 바로 그 아래에는 직전에 이 함수를 호출한 함수가 자리한다. 모든 함수는 변다른 지시가 없으면 스택 공간에서 동적을 관리된다. 단 하나 예외가 있는데 바로 메서드 main()이다.
메서드 main()은 정적 메모리에 미리 자리를 잡아두고 시작한다.이후 main()에서 호출된 메서드들은 차례대로 스택 영역에서 위로 쌓이며 수행하다가 수행이 끝나면 스택에서 지워진다. 임의의 함수가 호출되어 수행을 시작하면 스택 영역에 해당 함수가 호출되면서 넘겨받은 파라미터, 지역 변수 등을 공간에 할당 받는다. main()을 다른 함수들 처럼 스택 영역에 올려 수행하는 것도 가능하겠지만 자바에서는 메소드 main()을 이와 같은 방식으로 활용하기로 정한 것이다.
힙 영역에는 수행 중에 (자바의 new나 C의 malloc을 통해서)생성괸 배열이나 객체가 저장된다. 가지비 컬렉터가 청소하는 대상이 되는 공간이다. 할당받았다가 더 이상 쓰지 않는 메모리 공간을 회수하는 것을 가비지 컬렉션(Garbage Collection)이라 한다. C와 같은 언어에서는 더 이상 쓰지 않는 메모리 공간을 회수하는 일이 프로그래머의 책임임이지만 자바는 가상머신에서 알아서 처리해준다. 스택 영역과 힙 영역의 관계를 잘 나타내는 것이 가지비 컬렉션이기도 하다. 다시 말해, 가비지 컬레션이란 '힙 영역의 배열이나 객체 가운데 스택 영역(정확히 말하면 스택 영역과 메서드 main())에서 도달 불가능한 것을 제거 하는 것이다.'
LinkedList t = new LinkedList();
한가지 예를 더 들어 보자. 어느 함수(M이라 하자)에서 위 와 같이 LinkedList 객체를 생성했다면, 생성된 객체는 힙에 저장된다. 레버런스 변수 t는 해당 함수 M의 스택 영역에 저장된다. 함수 M이 끝나면 이 객체를 가리키는 레퍼런스 변수 t가 없어 지므로 스택 영역에서 해당 객체에 도달하는 끈이 없어진다. 따라서 이 객체의 메모리는 회수(가비지 컬렉션)된다. 예외적으로 그 사이에 어떤 정적 변수가 같은 객체를 레퍼런스하게 되었다면 이 객체는 아직 끈이 남아 있어 회수되지 않는다.
자바에서 병렬 처리를 위한 쓰레드Thread를 제공한는데, 예를 들어 10개의 쓰레드를 생성해서 동시에 수행하도록 할 수있다. 이들은 독립적으로 수행되기 때무에 가각의 스택을 할당받는다. 쓰레드 1개당 독립적 스택 공간이 1개 씩 존재하게 된다. 쓰레드가 끝나는 즉시 해당 스택 공간은 반환된다.
[출처 - 쉽게 배우는 자료구조 with 자바, 저 문병로]
https://www.hanbit.co.kr/store/books/look.php?p_code=B7033044159'개발언어 > JAVA' 카테고리의 다른 글
JAVA 자료구조 (배열) - 주식을 사고팔기 가장 좋은 시점 (1) 2023.12.22 JAVA 람다와 스트림 - 컬렉션 프레임워크(CF)와 함수형 인터페이스 (1) 2023.12.18 JAVA람다와 스트림 - Predicate의 결합 (1) 2023.12.18 JAVA 람다와 스트림 - java.util.funtion 패키지 (1) 2023.12.18 JAVA 람다와 스트림 - 함수형 인터페이스 (0) 2023.12.15