언어 공부 내용 정리/Java

자바 문자열(String) / 스택(stack)과 힙(heap)

wosrn 2023. 5. 26. 17:36

문자열(String)이란

문자열 : 1개 이상의 문자들로 구성된 자료형 , java에서 문자열을 나타내는 자료형은 String이다

 

문자열(String)의 사용

1) 리터럴 표기

String a = "java" ;

-" " 안에 넣으면 문자열, ' '안에 넣으면 문자로 인식

-가독성에 이점이 있고 컴파일 시 최적화에 도움을 줌

-객체 생성없이 고정된 값을 그대로 대입하는 방법

-heap 영역 안에 있는 String Constant Pool에 저장된다

-String Constant Pool에 이미 존재하는 문자열이라면 같은 주소값을 공유한다 = 같은 내용의 문자열을 서로 다른 변수 a,b에 저장하면 String Constant Pool 내에 같은 메모리 주소를 가리킨다 -> new 키워드 사용할때와의 차이

=> 문자열 사용 시에 new 를 잘 안쓰고 리터럴을 일반적으로 쓰는 이유(메모리 절약)

 

2)new 사용

String a = new String("java");

-new 키워드는 객체를 만들 때 사용

-새로운 String 객체를 만든다

-heap 영역에 할당됨

-새로운 인스턴스를 생성할 때마다 heap 영역에 객체가 새로 생긴다(같은 내용을 가진 문자열을 또 생성하더라도 리터럴과 달리 서로 다른 객체가 생성됨)->리터럴과의 차이

 

String 클래스의 intern 메소드

-jvm에서 관리하는 문자열 풀에서 해당 문자열을 조회하여 존재하는 경우 반환, 아닌 경우 풀에 문자열을 등록하고 해당 문자열을 반환하는 메소드

-String pool에서 리터럴 문자열이 이미 존재하는지 체크하고 존재하면 해당 문자열을 반환하고, 아니면 리터럴을 String pool에 넣어준다

-String을 리터럴로 선언하면 내부적으로 intern 메소드가 호출된다

-intern()을 이용하면 equals()없이 ==연산자만으로 문자열 비교가 가능하다 ! (속도나 메모리 향상을 위해 equals()가 아닌 == 연산자의 사용이 필요할 때 주로 사용하는 방법)

String a = "hi";
String b = new String("hi");
String c = b.intern()

System.out.println(a==b); 
System.out.prrintln(a==c);

 

위와 같이 코드를 입력하면 각 변수들의 메모리 구조는 다음 그림처럼 자리잡게 된다.

c는 b의 문자열 값인 "hi"가 string pool에 있는지 체크 -> 있기 때문에 해당 문자열을 반환함 = a와 같은 주소값을 공유하게 됨 

-> a==b는 false지만(주소값이 다르므로) a==c는 true가 나온다. 이것이 intern()메소드를 이용하여 equals()없이 문자열을 비교하는 방법이다

 

일반적인 참조자료형과는 다른 String 클래스의 특징

1)String은 불변한 객체 : 객체 내의 값 변경이 불가능 -> 값 변경시 새로운 객체를 생성하여 작성 (hashCode()메소드를 이용해 실제로 변수가 가지고 있는 주소값을 찍어보면 알 수 있다)

2)리터럴을 바로 입력한 데이터는 문자열이 같은 경우 하나의 객체를 공유

 

원시(primitive) 자료형 (기본 자료형)

-byte, short, int, double, long, float, boolean, char 의 기본 자료형들을 원시 자료형(혹은 기본 자료형)이라고 부른다. 이런 원시 자료형은 new키워드로 그 값을 생성할 수 없고 리터럴 표기 방식으로만 값을 세팅할 수 있다

-원시 자료형은 값을 가지고 있는 변수이다

-반드시 사용하기 전에 선언되어야 한다

 

 

참조(reference) 자료형

-참조 자료형은 주소를 가지고 있는 변수로, 변수에 (실제 객체가 아닌) 객체의 주소를 저장한다

-기본 자료형이 아닌 경우 참조형에 해당한다

-대표적으로 클래스, 인터페이스, 배열 등이 있다

-String은 원시 자료형에 해당하지 않고, 참조 자료형에 해당한다

 

스택과 힙

-자바의 메모리는 크게 class,static,final,메소드 영역 / 스택 영역 / 힙 영역으로 나뉜다

 

스택(stack)

-기본,참조 자료형 상관 없이 모든 지역변수가 위치하는 영역

-원시타입의 데이터가 값과 함께 저장됨(참조값이 아닌 실제값을 스택에 저장)

-힙 영역에 생성된 object 타입의 데이터의 참조변수가 저장됨

-메소드가 호출되면 메모리에 할당되고, 메소드가 종료되면 후입선출 구조로, 나중에 쌓인 값이 먼저 정리되며 차례대로 스택에 할당되었던 값들이 자동으로 종료(해제)된다

-스택 메모리 공간은 값을 초기화하지 않으면 빈 공간으로 존재 -> 해당 변수를 출력하면 오류가 발생

-String 등 객체 타입의 변수와 그에 따른 데이터를 생성했을 시 참조변수는 스택에 저장되지만 스택 영역의 참조변수는 할당된 실제 데이터값을 가지고 있는 것이 아니라 힙에 선언된 String을 레퍼런스 한다

 

힙(heap)

-object타입의 데이터가 저장됨(new 키워드로 생성된 객체, 배열)

-대체로 생명주기가 긴 데이터가 저장된다

-모든 참조 자료형의 실제 데이터(객체)는 힙 메모리에 저장됨

-힙 메모리에 객체를 생성하기 위해서는 new 키워드 사용

-애플리케이션의 모든 메모리 중 stack을 제외한 부분

-자동으로 관리되지 않는 메모리 영역(cpu가 엄격히 관리하지 않는 메모리 영역)

-String a = "java" ; 로 String을 생성하면 a는 스택에, java는 힙에 저장됨 / 스택에 저장된 a는 힙의 String 타입의 java라는 데이터를 레퍼런스 하게 됨

-힙 메모리 공간은 어떤 상황에도 빈 공간 존재 x, 값을 주지 않으면 컴파일러가 값을 강제로 초기화

 

가비지 컬렉터(garbage collector)

string a = "ap" ; 

a += "ple";

이라는 코드를 치면

스택에는 a, 힙에는 String ap 가 저장됨, 이후 a에 ple을 더하면 기존의 string ap에 ple이 더해지는 것이 아니고 힙 영역에 새롭게 string apple이 생기고 스택의 a 가 이를 레퍼런스하게 됨! -> 기존의 ap라는 문자열은 레퍼런스하고 있는 오브젝트가 없는 상태, 즉 언리처블 오브젝트(스택에 도달할 수 없는 힙 영역의 객체)가 됨 -> jvm의 가비지 컬렉터가 언리처블 오브젝트를 정리함 -> 최종적으로 힙 영역에 기존의 ap는 없어지고 string apple 만이 남고, 스택 영역의 a가 이를 레퍼런스 하는 상태가 됨