[OOP] 자바 vs 구조적/절차적 프로그래밍
일반적으로 프로그램이 메모리를 사용하는 방식은 아래
의 사진과 같이 코드 실행 영역과 데이터 저장 영역으로 구분된다.
하지만 객체 지향 프로그램에서는 데이터 저장 영역이 더 세분화된다.
위처럼 데이터 저장 영역을 스태틱 영역, 스택 영역, 힙 영역으로 나뉜다.
기본적인 OOP에 대한 지식이 있다고 가정하고 간략하게 구조적/절차적 프로그래밍, OOP프로그래밍에 대해 설명하겠다.
구조적/절차적 프로그래밍
마치 물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시 되는 프로그래밍
대표적인 언어로는 C, FORTRAN이 있다.
구조적/절차적 프로그래밍
구조적 프로그래밍의 장점은 실행 속도가 빠르고, 프로그램의 흐름을 쉽게 추적할 수 있다는 장점이 있지만
단점으로는 유지보수가 어렵고, 실행순서가 명확하기 때문에 코드의 순서가 바뀌면 동일한 결과가 기대하기 어렵다.
객체 지향 프로그래밍(OOP)
객체 지향은 실제 세계를 모델링하여 소프트웨어를 개발하는 방법
대표적인 언어로는 Java, C++, C#, Python, Kotlin등이 있다.
객체 지향 프로그래밍
OOP의 장점으로는 모듈화, 캡슐화로 인해 유지보수가 편하고, 재사용에 용이하다
단점으로는 코드를 설계하고 작성하는데 구조적 프로그래밍의 비해 많은 시간이 소요된다.
goto문을 다들 많이 들어봤을거라 믿는다.
여기서 절차적 프로그래밍을 한마디로 정리하면 goto문을 사용하지 말라는 의미이다.
goto문을 자바에서 사용하게 되면 프로그램의 실행 순서가 인간이 이해하기에 너무 복잡해질 수도 있어서
JAVA에서는 사용하지 못하게끔 예약어로 설정해놨다.
구조적/절차적 프로그래밍은 함수를 이용한 프로그래밍이고, 객체 지향 프로그래밍은 메소드를 이용한 프로그래밍이다.
함수(Function) vs 메소드(Method)
둘은 똑같은 역할을 하지만 굳이 차이점을 말하라고 하면 함수는 클래스나 객체와 아무 관계가 없지만
메소드는 반드시 클래스 정의 안에 존재해야 한다.
즉, 객체 지향 프로그래밍은 클래스 외부에 존재할 수 있는 것은 없다.
T메모리 구조
만약 하나의 .java파일이 있고, main메소드가 실행될 때 메모리 구조를 파악해보자
T메모리 구조는 위의 사진처럼 스태틱, 스택, 힙 영역으로 나뉘어져 있다.
여기서 아래의 코드가 있다고 가정하자.
public class Oop{
public static void main(String[] args){
System.out.println("hello");
}
}
우선 JRE는 프로그램상에 main()메소드가 있는지 확인하고 존재한다면 JVM을 작동시킨다.
JVM의 전원이 켜지면 제일 먼저 하는 일이 전처리를 하는 것이다.
전처리 과정 중 제일 먼저 java.lang패키지를 스태틱영역에 올린다.
그런 다음 JVM은 개발자가 작성한 모든 클래스와 import한 패키지를 스태틱 영역에 올린다.
여기서 main()메소드는 아직 실행이 된 것이 아니다.
위에서 설명한 작업이 바로 JVM의 전처리 작업이다.
위의 전처리 작업이 다 끝나면 main()메소드를 스택영역에 할당해야한다.
위처럼 바로 System.out.println()이 실행되는 것이 아니라 main()메소드를 먼저 스택 영역에 할당한 후 메소드들의 인자들의 공간을 우선적으로 할당한다.
이런식으로 하나의 클래스가 실행되면 위처럼 T메모리구조가 변화하고 만약 main()메소드가 여는 중괄호로 열어서
시작하고, 닫는 중괄호를 만나게 되면 스택 영역에 있는 main()메소드는 소멸된다.
여기서 main()메소드는 프로그램의 시작점이기 때문에 main()메소드가 종료되면 JRE는 JVM을 종료하고 JRE도
OS영역 메모리에서 사라진다.
<프로그램 메모리 구조>
프로그램 시작 -> JRE작동 -> JVM실행 -> main()실행 -> main()종료 -> JVM중지 -> JRE작동종료 -> 프로그램 종료
변수(지역변수, 클래스변수, 객체변수)
변수는 T메모리 구조 기준으로 어디에 존재할까?
정답은 바로 3군데 다 존재한다.
변수에는 지역, 클래스, 객체변수 총 3가지 종류로 나뉜다.
위처럼 변수의 종류에 따라 할당되는 영역이 서로 다르다.
- Local Variable(지역변수) -> 지역변수는 메소드 안에 생성되는 변수로서 해당 메소드가 종료되어 메모리에서 삭제되면 변수 또한 같이 삭제된다.
- Class Variable(클래스 변수) -> 전역변수라고도 하며, 한번 생성이 되면 JVM이 종료될 때 사라진다.
- Object Variable(객체 변수) -> 객체 멤버 변수들은 객체와 함께 GC(가비지 컬렉터)라는 회수기와 함께 사라진다.
위의 코드를 보면 main()메소드에 k변수가 있고 int m=serve(k)에서 k 지역변수를 serve()메소드에 인자로 보내고 있다.
serve()메소드는 k변수를 인자로 받고 있는데 과연 serve()메소드에서 k값을 바꾸면 main()메소드의 k값이 변경이 될까?
정답은 'No'이다.
왜냐하면 T메모리 구조를 기반으로 생각하면
main()메소드에 있는 k와 serve()메소드 안에 있는 k는 서로 다른 영역에 있기 때문에 serve()메소드에서 k값을 변경하여도
main()메소드의 k값은 변경되지 않는다.
이것을 바로 Call By Value(값에 의한 호출) 이라고 한다.
<전역 변수>
코드 어디서든지 접근할 수 있다고 해서 전역변수이고, 여러 메소드들이 공유할 수 있어서 '공유 변수' 라고도 함
그럼 전역변수를 많이 사용하는 것이 더 좋을까?
사람마다 다르겠지만 나는 아니라고 생각한다.
왜냐하면 전역변수는 정의대로 어디에서든 공유가 가능하기 때문에 A라는 메소드에서 staticVariable변수의 값을 수정하고
B라는 메소드에서 staticVariable값을 수정도 가능하고 출력도 가능하다.
하지만 간단한 코드에서는 괜찮지만 수천, 수만 줄이 되는 코드에서 전역변수를 이용하게 되면 값이 변해도
어디서 변했는지 알 수가 없기 때문에 원인을 찾는데 아주 오래 걸리게된다.
즉, 나는 전역변수는 최대한 안 쓰는 것이 좋다고 생각한다.
멀티 스레드 vs 멀티 프로세스
스태틱 영역 | ||
스택 영역 - 메소드들의 놀이터 | 힙영역 | |
스레드 | 스레드 |
<멀티 스레드>
멀티스레드를 T메모리 구조로 보았을 때 스택 영역을 스레드 개수만큼 분할해서 쓰는 것을 의미한다.
멀티 프로세스를 T메모리 구조로 보면 위처럼 다수의 T메모리를 갖는 구조이다.
따라서 멀티 프로세스는 각각 프로세스마다 T메모리가 있기 때문에 서로 참조할 수 없다.
반면에 멀티 스레드는 스택영역에서 스레드 만큼 분할하였기 때문에 한 스레드에서 다른 스레드의 스택영역에 접근할 순없지만 스태틱영역과 힙영역은 공유하는 구조이기 때문에 멀티 프로세스보다 메모리를 적게 사용하는 구조이다.