자바(java)_컴퓨터의 메모리 구조,Static
▶컴퓨터의 메모리구조
-. 코드 영역(고정영역)
프로그램의 코드가 저장되는 영역이다.
이 영역에 저장된 명령어들을 CPU가 하나씩 가져가 실행한다.
-. 데이터 영역(고정영역)
전역변수와 static으로 선언되는 변수가 할당된다.
이 영역에 할당되는 변수들은 프로그램 시작과 동시에 메모리 공간이 할당되어 종료될 때 까지 남아있게된다.
-. 힙 영역(동적영역)
프로그래머가 원하는 시점에 변수를 할당하고 소멸시키는 영역
메모리 동적 할당시 사용되며 객체가 생성되는 영역이다.
그래서 객체가 생성이 되었다가 쓰임을 당하면 없어진다.
-. 스택 영역(동적영역)
함수가 실행될 때 사용되는 파라미터와 지역 변수에 대한 메모리 공간이다.
함수의 종료와 함께 소멸된다.
▶하나의 프로그램이 사용하는 메모리 영역
고정영역
프로그램이 종료될 때 까지 고정적으로 남아있는 것이라는 뜻이다.
프로그램이 실행되면 실행파일이 메모리에 로드되면서, 실팽파일의 용량만큼 RAM을 사용한다.
실행파일의 크기는 변할 수 없음으로 이 영역의 크기는 고정크기를 가진다.
동적영역
프로그래머가 new 키워드를 사용해서 객체나 배열을 생성하면 사용된다.(힙영역)
또 다른 경우는 메서드가 호출되는 동안 사용될 파라미터와 지역변수가 생성된다 (스택영역)
메서드가 종료되거나 객체가 더 이상 사용되지 않으면 생성된 변수나 객체는 메모리에서 사라지므로, 이 영역은 유동적인 크기를 가지게 된다.
▶Static
만약 우리가 게시판을 개발을 한다면 밑의 코드처럼 멤버변수를 먼저 생성할 것이다.
//하나의 게시물을 표현하기 위한 클래스
public class Article{
private int num; //글번호
private String title; //제목
private String regDate; //날짜
}
근데 고객사에서 전체 현재 카테고리의 총 글의 수와 게시판의 카테고리 정보를 표현을 해달라고 추가요청 하였다.
그럼 우리는 Aritcle 밑에 이 요구사항을 충족시킬 수 있는 변수 2개를 추가하는 경우가 생길 것이다.
//하나의 게시물을 표현하기 위한 클래스
public class Article{
private int count; //전체글수
private int category; //카테고리
private int num; //글번호
private String title; //제목
private String regDate; / /날짜
}
이러면 무슨 문제가 생길까?
num과 title, regDAte는 변경이 되는 것이 맞으나,, count3등은 동일한건데 똑같은 데이터가 또 쌓이는 것이다. 멤버변수는 모든 객체가 독립적으로 가지는 고유 데이터이기때문에 게시물 수라는 공유 데이터를 모든 게시물이 가지게된다. 즉, 각각의 객체가 중복된 데이터를 가지게 되는건데 이는 데이터낭비라고 볼 수 있다.
그럼 공통된 멤버변수를 저렇게 묶어버리면 어떨까? 이때 사용하는 키워드가 Static이다.
▶객체간의 공유 자원을 표현하는 Static 키워드
클래스를 설계할 때, 멤버변수 중 모든 객체에서 공통적으로 사용해야 하는 값에 static을 붙힌다고 생각하면 된다.
//하나의 게시물을 표현하기 위한 클래스
public class Article{
private int count; //전체글수
private int category; //카테고리
private int num; //글번호
private String title; //제목
private String regDate; / /날짜
}
위의 코드에 공통적으로 들어갔으면 하는 변수는 전체글 수와 카테고리
private static int count; //전체글수
private static int category; //카테고리
그럼 데이터유형 앞에 static을 붙혀준다. static이 붙은 멤버변수는 객체의 개수에 상관 없이 단 하나만 생성된다. 즉, Article이라고 하는 클래스를 100개를 만들어서 게시글을 100개를 작성을 하더라도 static이라는 키워드가 붙었기에 count 와 category는 내부적으로 한개만 할당이 되고 count를 100으로 할당을 했을 떄, 모든 count는 100이라는 모든 객체가 그 값을 공유하기 때문에 메모리를 보다 효율적이게 관리할 수 있다.
단, 고정된 영역은 한정되어있기때문에 게시판을 만들고 만들수록 고정된 영역은 없어지는것이다. 그래서 무분별한 Static의 사용원 권장하지 않고 꼭 필요할 때만 사용하도록 하자
▶프로그램의 메모리를 사용하는 순서
최초 실행시 고정 영역에 실행파일만큼의 메모리를 점유하며 프로그램이 각종 동작을 수행하는 동안 동적 영역을 사용한다.
▶멤버변수와 static 변수의 차이점
Static(count,category)은 일반 멤버변수(title,regDate)와 동일한 클래스(Article)를 통해서 생성되는 객체임이 맞으나 이 둘은 정확하게는 전혀 다른 존재이다.
우선 가장 큰 차이점은 각자 저장되는 데이터공간이 다르다는 점이다. Static과 멤버변수는 Ariticle이라고 하는 특정 클래스에 선언이 되어 있지만, Static이 저장이 되는 즉, 쓰여지는 메모리의 영역은 데이터영역, 멤버변수는 Heap 메모리영역에 저장이 된다.
소스코드상에는 Static과 멤버변수는 모두 Article이라는 하나의 클래스에 선언되어 있지만 프로젝트 입장에서 보았을 때는 이 둘은 저장공간이 다르게 저장되어있는 전혀 다른 존재라는 것이다.
이말이 의미하는 것은 하나 더 있다.
static은 객체를 생성하지 않아도 접근이 가능하다.
우리는 클래스 안의 변수에 접근을 하기 위해선 객체를 생성해줘야한다.
그러나 Static은 프로그램을 실행하자마자 실행되는 고정영역에 저장되어있다.
즉, 객체가 없어도 프로그램이 실행이 되자마자 count와 category는 자동으로 생성이 되기 때문에 객체를 따로 설정을 하지 않아도 되고 static 변수는 객체의 이름을 통해 접근하는 것이 아니라 클래스의 이름을 통해 접근을 해야한다는 것이다.
Static변수 | 일반 멤버변수 |
클래스 이름을 통해 접근 | 객체 이름으로 접근 |
메모리의 고정영역 중 데이터영역에 저장 | 메모리의 동적영역 중 Heap 메모리 영역에 저장 |
프로그램이 실행되자마자 존재 | 객체를 생성해야지만 존재 |
그런데 static 변수가 선언된 클래스 안에서는 변수 이름으로 직접 접근이 허용이 된다는 예외 역시 존재한다.
//Article 클래스 및 다른 클래스에서 접근하는 경우
Article.count = 9;
Article.category = "공지사항";
//Article 클래스에서 접근하는 경우
count = 15;
category = "Q&A게시판";
이는 static을 이용하기 위해서는 Article.count처럼 변수명으로 반드시 접근을 해주어야하는데 본인이 선언이 되어있는 Article안의 클래스 안에서 상용을 원한다면 count처럼 변수명으로만 접근이 가능하다.
▶static 변수와 static 메서드
메서드에도 static이라고 하는 키워드를 붙혀줄 수 있다.
우린 A라고 하는 클래스 안에다가 변수를 선언하고 메서드도 선언을 하기도 할 것이다.
static 메서드는 보통 static 변수에 private를 선언할 때 사용된다.
예를들어 우리가 name의 값을 static으로 세팅하였다. 근데 우리는 name이라는 값을 받을 때 특수문자는 받지 않는다던지, 영어는 불가능한다던지 등의 조건을 걸고 싶다. 이 때 우리가 사용을 해야할 메서드가 setter이다. 값을 받을 때 조건문을 걸어 받아오는 값을 필터링을 해줘야하기때문에 private를 선언을 하는 것이다.
또한 우리가 name을 어느곳에서든 name을 가져다쓰게 static으로 열어두었지만 접근이 가능하다고 해서 어떤 누군가가 name의 기능을 작위적으로 변경하도록 완전히 오픈할 필요는 없다.
즉, name은 static으로 선언되어 name이라는 변수를 어느곳에나 사용할 수 있게 하되 getter,setter를 사용하여 name에 대한 무결성을 지켜준다라고 정리할 수 있다.
▶ static getter메서드와 setter메서드
static 메서드에 대한 getter와 setter 메서드를 예제로 좀 더 자세히 알아보자
public class Article{
private static int count;
public static void setCount( int count ){
Article.count = count;
}
public static int getCount(){
return Article.count;
}
}
private로 설정한 count라는 static변수가 있다. 여기서 count는 고정메모리 영역에 선언이 되어있으니 이 static변수에 접근을 하고 값을 넣거나 변경하기 위해선 setter와 getter메서드가 메모리 영역에 올라가줘야지만 count 변수에 접근이 가능하다. 즉, 객체를 생성을 해야한다는 의미이다.
그런데 static을 사용하는 이유는 쉽게 접근을 하고 해당 코드를 편하게 사용하기 위해 선언하는건데 이렇게 매번 객체를 생성하게 되면 몹시 번거로울것이고 취지와 맞지않는다. 그래서 사람들은 생각했고 static에 접근하는 getter와 setter에도 static을 붙히기로 한다!
public class Article{
private static int count;
public static static void setCount( int count ){
Article.count = count;
}
public static static int getCount(){
return Article.count;
}
}
이렇게 getter와 setter에 static을 붙히게되면 이 메서드들도 자연스럽게 count와 동일한 공간인 고정영역에 저장이 되기 때문에 쉽게 접근이 가능하다.
그러므로 객체의 생성과 상관없이 static 변수에 접근하기 위한 메서드를 getter와 setter에 static을 붙힘으로 구현한것이다.
▶ static 메서드 사용시의 제약 사항
클래스에서 정의하는 일반 메서드들은 객체의 생성과 동시에 동적 메모리 영역에서 활성화 된다. 그러나 고정 메모리 영역의 자원들은 프로그램이 실행되자마자 생성이 되어 동적 메모리 영역의 입장으로는 고정 메모리 영역의 자원들은 항상 존재한다. 그러나 고정 메모리 영역의 자원들은 동적 메모리의 자원들이 항상 존재한다는 보장을 받지 못한다.
이러한 메모리 영역의 차이 때문에 static 메서드는 동적 메모리 영역의 멤버변수를 사용하거나, static이 아닌 일반 멤버함수를 호출할 수 없다.
정리를 해보자면!
[고정영역]
-> static 변수와 static 멤버함수가 생성된다.
-> 이 영역의 자원들은 프로그램이 실행되는 동안 항상 존재한다.
-> 동적 메모리 영역의 자원들은 항상 존재하는 것이 아니기 때문에 static 멤버함수는 동적 메모리 영역의 멤버변수를 사용하거나, static이 아닌 일반 멤버함수를 호출할 수 없다.
[동적영역]
-> 객체, 객체 안의 멤버변수, 메서드, 메서드가 호출되었을때 사용되는 지역변수 등이 생성된다.
-> 이 영역의 자원들은 생성과 제거가 유동적으로 반복된다.
-> static 자원은 항상 존재하는 것이므로 동적 영역에서는 static 변수를 활용하거나, static 멤버함수를 호출할 수 있다.
▶ static 메서드의 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package Static;
public class Article {
private static int count;
private static String category;
private int num;
private String title;
private String regDate;
public Article(int num, String title, String regDate) {
super();
this.num = num;
this.title = title;
this.regDate = regDate;
count++;
}
|
cs |
위의 코드는 하나의 게시글을 표현하기 위한 자바빈즈라고도 할 수 있다.
count 는 전체 게시물 수를 표현하기 위한 데이터고 category는 게시물을 분류하기 위한 데이터로서 모든 객체가 공유할 필요성이 있다고 생각하여 static으로 공유해두었다.
근데 count는 전체 게시물 수를 표현하기 위한 것이다. 지금은 멤버변수가 3개밖에 없어서 상관 없지만 만약 100개, 1000개 수많은 게시글을 처리해야하는 count라면 우리는 어떻게 처리를 해야할까?
지금 모든 값을 파라미터를 가지는 Article이라는 생성자로 가지고 오고있다. 값을 받으려면 무조건 Article이라는 클래스를 통과해야만하는건데 그럼 제일 마지막에 count ++; 를 넣게되면 전체 게시글의 수를 확인할 수 있지 않을까
값을 넘긴다는 것은 값을 받을 공간이 있다는 것이고 count는 그 공간들을 세는 기능을 해야하는 변수이다.
그럼으로 Article 마지막에 생성을 하면 간단하게 count라는 기능을 실현시킬 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class Main01 {
public static void main(String[] args) {
Article.setCategory("자유게시판");
Article a1 = new Article(1, "첫 번째 글 제목", "2022-03-24");
Article a2 = new Article(2, "두 번째 글 제목", "2022-03-25");
Article a3 = new Article(3, "세 번째 글 제목", "2022-03-25");
// 출력 결과에서 모든 객체가 동일한 count값을 갖는다.
System.out.println( a1.toString() );
System.out.println( a2.toString() );
System.out.println( a3.toString() );
System.out.println("--------------------------");
Article.setCategory("공지사항");
System.out.println( a1.toString() );
System.out.println( a2.toString() );
System.out.println( a3.toString() );
}
}
|
cs |
그럼 이번엔 Main함수를 봐보자
Static은 객체를 만들 필요가 없음으로 Article.setCategory("자유게시판")으로 데이터를 넘겨주었고
일반 멤버변수는 객체를 무조건적으로 생성을 해야 존재하니 Article a1 new Article(1."첫번째 글 제목","2022-3-24)로 데이터를 넘겨준 것을 확인할 수 있다.
또한 6,7,8행에 a1,a2,a3이 하나씩 만들어져서 Article에 넘어갈 때 마다 count는 하나씩 올라가서 게시물 수를 계산할 것이라는 걸 우리는 알고있다. 출력을 한번 시켜보자.
1
2
3
4
5
6
7
8
|
@Override
public String toString() {
return " 글 분류 = " + category + ", 글 전체수 = " + count +
", Article [num=" + num
+ ", title=" + title
+ ", regDate=" + regDate + "]";
}
|
cs |
Article클래스에서 출력을 위한 toString메서드를 넣어서 출력을 시키면?
Static 메서드들은 값이 고정되어있고 일반 멤버변수의 데이털값만 바뀌어 잘 출력이 되는 것을 볼 수 있다.
count++역시 잘 구현되어있다.