JAVA 특징
- 플랫폼 독립성
- Java 코드는 컴파일되면 바이트코드(Bytecode)로 변환됨.
- 특정 운영체제에 의존하지 않고, JVM(Java Virtual Machine) 이 설치된 환경에서 실행 가능.
- 객체 지향 프로그래밍(OOP)
- 캡슐화, 상속, 다형성, 추상화 등의 OOP 개념을 지원.
- 코드의 재사용성 과 유지보수성 이 뛰어남.
- 멀티스레딩 지원(Thread-Multi)
- 여러 작업을 동시에 수행할 수 있는 멀티스레딩(Multithreading) 기능 제공.
- 병렬 처리를 쉽게 구현 가능.
- 가비지 컬렉션(Garbage Collection)
- Java는 자동 메모리 관리 를 지원.
- 사용되지 않는 객체를 GC(Garbage Collector) 가 자동으로 제거하여 메모리 누수를 방지.
- 보안(Security)
- 바이트코드 검증 등으로 보안성이 높음.
- 예외 처리(Exception Handling) 지원
- try-catch-finally, throws 를 활용한 예외 처리 기능 제공.
- 프로그램의 안정성을 높이고, 예외 상황을 효과적으로 관리 가능.
JVM의 역할 (기능)
- 바이트코드 실행
- Java 컴파일러(javac)가 변환한 바이트코드(.class 파일) 를 실행.
- 운영체제(OS)와 독립적으로 동작하여 플랫폼 독립성 제공.
- 메모리 관리 및 가비지 컬렉션(GC)
- 프로그램 실행 시 Heap, Stack, Method Area, PC Register 등의 메모리를 관리.
- 사용하지 않는 객체를 GC(Garbage Collector) 가 자동으로 제거.
- 클래스 로딩(Class Loading)
- 실행 시 필요한 클래스를 클래스 로더(Class Loader) 가 동적으로 로드.
- 런타임 환경 제공
- Java 프로그램이 실행되도록 JRE(Java Runtime Environment) 를 제공.
- 표준 라이브러리(API)와 함께 동작.
- 바이트코드 검증(Security & Verification)
- 바이트코드가 변조되지 않았는지 검사하고, 보안 위반 여부를 확인.
JVM 실행 과정
.java파일로 코드 작성 >> javac로 컴파일 >> .class 파일 생성 후 바이트 코드로 변환 >> .class 파일을 메모리에 로드 >> 바이트 코드 검증 >> 기계어로 변환 >> 실행
printf 대신 println을 써야하는 이유
printf는 포맷 지정자를 사용하기 때문에 포맷 문자열 공격을 받을 수 있음
System.out.printf("Hello, %s!\n", "World"); // "Hello, World!\n" 출력
printf : 일반적으로 위와 같이 동작하는데, %s같은 포맷 지정자를 외부 입력과 함께 사용하는 경우 문제 발생 가능
String userInput = "%x %x %x %x";
System.out.printf(userInput);
printf(인수가 없는 경우) : printf는 인수를 찾으려고 스택에서 임의의 메모리를 읽어옴 (인수를 안 줬는데도 %x를 해석하려 하니, 스택에 저장된 데이터가 그대로 출력됨)
String userInput = "%x %x %x %x";
System.out.println(userInput); // 그냥 "%x %x %x %x" 그대로 출력됨
println : println의 경우에는 포맷 지정자를 해석하지않고 단순히 문자열만 출력함
public static void main(String[] args)
public
- 접근 제어자(Access Modifier)
- public이기 때문에 JVM이 어디서든 접근 가능해야 함.
- 만약 private으로 하면, JVM이 실행할 수 없어서 프로그램이 동작하지 않음.
static
- static 키워드는 객체를 생성하지 않고도 호출 가능하다는 의미.
- 만약 static이 없으면, main()을 호출하려면 먼저 클래스의 객체 인스턴스를 만들어야 함.
- JVM이 main()을 실행할 때 빠르게 실행해야함 >> 객체 생성 없이 바로 호출해야 하기 때문에 static이 필요함.
void
- 반환 타입이 없다는 뜻. 즉, 이 메서드는 값을 반환하지 않음.
- 만약 int 같은 반환 타입을 지정하면, main() 실행 후 어떤 값을 반환해야 하는데, 그런 기능이 필요하지 않음.
main
- 메서드 이름으로, JVM이 프로그램 실행을 시작하는 진입점(entry point) 메서드.
- 이름을 바꾸면 실행되지 않음 (start(), execute() 등은 JVM이 찾지 않음).
- main이라는 이름으로 JVM에 미리 정의되어 있음
(String[] args)
- 문자열 배열을 args라는 이름의 매개변수로 받는다는 의미
자료형
primitive(기본) : 숫자형(정수형(byte, short, int, long), 실수형), 단일 문자형, 논리형
primitive(기본) | reference(참조) | |||
숫자형 | 단일 문자형 | 논리형 | 사용자가 정의하는 데이터 타입 | |
정수형 | 실수형 | char (2byte) | boolean (1byte) | (class, interface, enum) |
byte (1byte) | float (4byte) | String | ||
short (2byte) | System | |||
int (4byte) | double (8byte) | Object | ||
long (8byte) | PrintStream | |||
... |
default value
Java 프로그램 실행 과정
- Java 파일 컴파일 (javac)
- .java 파일을 javac로 컴파일하면 .class 파일이 생성됨. 이 파일은 바이트코드로 변환된 상태
- 클래스 로딩 (JRE의 클래스 로더)
- .class 파일이 JRE에 있는 클래스 로더에 의해 로드됨
- 클래스 로더는 클래스 파일이 JDK에서 컴파일된 것인지 확인(외부에서 심어졌을 수도 있기 때문에)하고, 이를 안전하게 메모리로 가져옴
- 바이트코드 검증 (바이트코드 베리파이어)
- 바이트코드 베리파이어는 로드된 바이트코드를 검수하여 올바른 코드인지 검사
- 검수를 통해 코드가 보안상의 문제를 일으킬 수 없도록 함
- 기계어 변환 (JVM)
- 검증된 바이트코드는 JVM에 의해 실행됨. JVM은 바이트코드를 기계어로 변환하여 실제 하드웨어에서 실행될 수 있도록 함
- 메인 메서드 실행
- 실행이 시작되면 main() 메서드가 호출
- static 멤버는 메모리에 초기화 됨
Java 메모리 구조
Java 프로그램이 실행될 때, JVM은 메모리를 여러 영역으로 나누어 관리함
- 스택(Stack) 영역
- 로컬 데이터 (지역 변수, 메서드 호출 정보 등)가 저장됨
- 힙(Heap) 영역
- 객체와 배열이 저장됨
- 이 영역은 동적으로 할당되며, 객체가 가비지 컬렉션에 의해 관리됨
- 힙 영역은 Young Generation과 Old Generation으로 나뉘고, Young Generation은 다시 Eden Space, Survivor Space, Old Generation으로 나눠짐
- Young Generation에서 새로운 객체가 생성되고, 객체가 살아남은 경우 Old Generation으로 이동
- 메소드(Method) 영역
- 클래스 정보, 메서드와 필드 정보, 정적 변수(static variables), 상수(final constants), 클래스의 런타임 데이터 등이 저장됨
- 정적 메서드, 정적 변수는 Method Area에 저장됨
- 상수 문자열(String literals)은 String Pool에 저장됨
- PC 레지스터 (Program Counter Register)
- 각 스레드마다 프로그램의 현재 실행 위치를 추적
- Non-Heap
- 힙 영역 외부에서 관리되는 JVM 메모리 영역
- 클래스 메타데이터, 정적 변수 등과 같은 데이터가 저장됨
데이터 타입의 메모리 할당과 성능 영향
- int age = 30;에서 30은 값이고, 이 값은 age라는 변수에 할당됨
- int는 4바이트 크기의 기본 데이터 타입이기 때문에, 30이라는 값을 4바이트 공간에 저장함
- 즉, JVM은 4바이트 크기의 공간을 할당하여 30을 그 공간에 저장하고, age 변수는 스택에 위치하게 됨
byte로 저장하지 않고 int로 저장하는 이유
- int는 4바이트를 차지하는데, 만약 1바이트로 저장하면, 3바이트가 낭비되게 됨
- JVM은 이와 같은 비효율적인 저장 방식 대신 적절한 메모리 크기를 사용하여 성능을 최적화하려 함
- 성능 최적화 과정을 한 번 더 수행하게 되기 때문에 오히려 성능이 안좋아질 수 있음
System.out.println(age)
System 클래스는 JVM이 클래스 로더를 이용해 메모리에 로드됨. 로딩은 System.out.println(age) 실행 시 필요한 클래스를 로드하고 초기화하는 과정. System 클래스의 out, in, err은 각각 4바이트 공간을 할당받고, 기본적으로 null로 초기화됨
finalize()
객체가 가비지 컬렉션 대상이 될 때 호출되는 메서드. 이 메서드를 오버라이딩하면, 객체가 수거되기 전에 특정 작업을 할 수 있음. 그러나 finalize()를 오버라이딩하면, 수거가 늦어질 수 있기 때문에 성능에 영향을 줄 수 있음.
가비지 컬렉션(GC)
가비지 컬렉터는 더 이상 사용되지 않는 **객체(쓰레기 객체)**를 자동으로 제거하고, 메모리 공간을 회수
- 사용되지 않는 객체는 가비지로 간주됨
- 가비지 컬렉터는 기존 메모리 공간에서 유효한 객체들을 다른 공간으로 옮김
- 비어있는 메모리 공간은 모두 정리됨
메서드를 작게 분할해야하는 이유
- 작은 메서드들은 빠르게 실행되고, 메모리에서 할당된 공간을 빠르게 해제함
- 가비지 컬렉터는 메서드 내에서 쓰지 않는 객체들을 찾아서 쓰레기 객체로 간주하고 처리함. 이를 위해 메서드가 빨리 종료되도록 적절하게 메서드를 분할하는 것이 좋음
MyStr name = new MyStr();
- new MyStr()로 생성된 객체는 힙 영역에 할당됨. 만약 MyStr 객체가 char 배열을 포함하고 있다면, 그 char 배열이 힙 영역에 저장됨
- name이라는 참조 변수가 스택 영역에 저장됨. 이 참조 변수는 객체의 메모리 주소를 저장하며, 객체 자체는 힙 영역에 존재함
System.out.println(name)
- name.toString()이 자동 호출되어 객체의 값이 출력됩니다. String 클래스에서는 toString() 메서드가 오버라이드되어 실제 문자열을 반환합니다.
- name.toString()이 자동으로 호출됨. 만약 MyStr 클래스에서 toString()을 오버라이드하지 않으면, 기본적으로 Object 클래스의 toString() 메서드가 호출되어 객체의 해시코드를 출력.
MyStr name = new MyStr("홍길동"); | String name = new String("홍길동"); | |
출력 | 오버라이드 하지 않은 경우 MyStr 객체의 해시코드가 출력 | "홍길동" 출력 |
'[LG 유플러스] 유레카 > Today I Learned' 카테고리의 다른 글
[TIL][02.10] 배열, 상속, 다형성, abstract (0) | 2025.02.10 |
---|---|
[TIL][02.07] String, Method, Arrays (0) | 2025.02.10 |
[TIL][02.05] implements, inheritance, override, abstract, generic, utility (0) | 2025.02.05 |
[TIL][02.04] Array Iteration, Map, TS (0) | 2025.02.04 |
[TIL][02.03] ES6, SSR, CSR, SSG, SOP, CORS (0) | 2025.02.04 |
JAVA 특징
- 플랫폼 독립성
- Java 코드는 컴파일되면 바이트코드(Bytecode)로 변환됨.
- 특정 운영체제에 의존하지 않고, JVM(Java Virtual Machine) 이 설치된 환경에서 실행 가능.
- 객체 지향 프로그래밍(OOP)
- 캡슐화, 상속, 다형성, 추상화 등의 OOP 개념을 지원.
- 코드의 재사용성 과 유지보수성 이 뛰어남.
- 멀티스레딩 지원(Thread-Multi)
- 여러 작업을 동시에 수행할 수 있는 멀티스레딩(Multithreading) 기능 제공.
- 병렬 처리를 쉽게 구현 가능.
- 가비지 컬렉션(Garbage Collection)
- Java는 자동 메모리 관리 를 지원.
- 사용되지 않는 객체를 GC(Garbage Collector) 가 자동으로 제거하여 메모리 누수를 방지.
- 보안(Security)
- 바이트코드 검증 등으로 보안성이 높음.
- 예외 처리(Exception Handling) 지원
- try-catch-finally, throws 를 활용한 예외 처리 기능 제공.
- 프로그램의 안정성을 높이고, 예외 상황을 효과적으로 관리 가능.
JVM의 역할 (기능)
- 바이트코드 실행
- Java 컴파일러(javac)가 변환한 바이트코드(.class 파일) 를 실행.
- 운영체제(OS)와 독립적으로 동작하여 플랫폼 독립성 제공.
- 메모리 관리 및 가비지 컬렉션(GC)
- 프로그램 실행 시 Heap, Stack, Method Area, PC Register 등의 메모리를 관리.
- 사용하지 않는 객체를 GC(Garbage Collector) 가 자동으로 제거.
- 클래스 로딩(Class Loading)
- 실행 시 필요한 클래스를 클래스 로더(Class Loader) 가 동적으로 로드.
- 런타임 환경 제공
- Java 프로그램이 실행되도록 JRE(Java Runtime Environment) 를 제공.
- 표준 라이브러리(API)와 함께 동작.
- 바이트코드 검증(Security & Verification)
- 바이트코드가 변조되지 않았는지 검사하고, 보안 위반 여부를 확인.
JVM 실행 과정
.java파일로 코드 작성 >> javac로 컴파일 >> .class 파일 생성 후 바이트 코드로 변환 >> .class 파일을 메모리에 로드 >> 바이트 코드 검증 >> 기계어로 변환 >> 실행
printf 대신 println을 써야하는 이유
printf는 포맷 지정자를 사용하기 때문에 포맷 문자열 공격을 받을 수 있음
System.out.printf("Hello, %s!\n", "World"); // "Hello, World!\n" 출력
printf : 일반적으로 위와 같이 동작하는데, %s같은 포맷 지정자를 외부 입력과 함께 사용하는 경우 문제 발생 가능
String userInput = "%x %x %x %x";
System.out.printf(userInput);
printf(인수가 없는 경우) : printf는 인수를 찾으려고 스택에서 임의의 메모리를 읽어옴 (인수를 안 줬는데도 %x를 해석하려 하니, 스택에 저장된 데이터가 그대로 출력됨)
String userInput = "%x %x %x %x";
System.out.println(userInput); // 그냥 "%x %x %x %x" 그대로 출력됨
println : println의 경우에는 포맷 지정자를 해석하지않고 단순히 문자열만 출력함
public static void main(String[] args)
public
- 접근 제어자(Access Modifier)
- public이기 때문에 JVM이 어디서든 접근 가능해야 함.
- 만약 private으로 하면, JVM이 실행할 수 없어서 프로그램이 동작하지 않음.
static
- static 키워드는 객체를 생성하지 않고도 호출 가능하다는 의미.
- 만약 static이 없으면, main()을 호출하려면 먼저 클래스의 객체 인스턴스를 만들어야 함.
- JVM이 main()을 실행할 때 빠르게 실행해야함 >> 객체 생성 없이 바로 호출해야 하기 때문에 static이 필요함.
void
- 반환 타입이 없다는 뜻. 즉, 이 메서드는 값을 반환하지 않음.
- 만약 int 같은 반환 타입을 지정하면, main() 실행 후 어떤 값을 반환해야 하는데, 그런 기능이 필요하지 않음.
main
- 메서드 이름으로, JVM이 프로그램 실행을 시작하는 진입점(entry point) 메서드.
- 이름을 바꾸면 실행되지 않음 (start(), execute() 등은 JVM이 찾지 않음).
- main이라는 이름으로 JVM에 미리 정의되어 있음
(String[] args)
- 문자열 배열을 args라는 이름의 매개변수로 받는다는 의미
자료형
primitive(기본) : 숫자형(정수형(byte, short, int, long), 실수형), 단일 문자형, 논리형
primitive(기본) | reference(참조) | |||
숫자형 | 단일 문자형 | 논리형 | 사용자가 정의하는 데이터 타입 | |
정수형 | 실수형 | char (2byte) | boolean (1byte) | (class, interface, enum) |
byte (1byte) | float (4byte) | String | ||
short (2byte) | System | |||
int (4byte) | double (8byte) | Object | ||
long (8byte) | PrintStream | |||
... |
default value
Java 프로그램 실행 과정
- Java 파일 컴파일 (javac)
- .java 파일을 javac로 컴파일하면 .class 파일이 생성됨. 이 파일은 바이트코드로 변환된 상태
- 클래스 로딩 (JRE의 클래스 로더)
- .class 파일이 JRE에 있는 클래스 로더에 의해 로드됨
- 클래스 로더는 클래스 파일이 JDK에서 컴파일된 것인지 확인(외부에서 심어졌을 수도 있기 때문에)하고, 이를 안전하게 메모리로 가져옴
- 바이트코드 검증 (바이트코드 베리파이어)
- 바이트코드 베리파이어는 로드된 바이트코드를 검수하여 올바른 코드인지 검사
- 검수를 통해 코드가 보안상의 문제를 일으킬 수 없도록 함
- 기계어 변환 (JVM)
- 검증된 바이트코드는 JVM에 의해 실행됨. JVM은 바이트코드를 기계어로 변환하여 실제 하드웨어에서 실행될 수 있도록 함
- 메인 메서드 실행
- 실행이 시작되면 main() 메서드가 호출
- static 멤버는 메모리에 초기화 됨
Java 메모리 구조
Java 프로그램이 실행될 때, JVM은 메모리를 여러 영역으로 나누어 관리함
- 스택(Stack) 영역
- 로컬 데이터 (지역 변수, 메서드 호출 정보 등)가 저장됨
- 힙(Heap) 영역
- 객체와 배열이 저장됨
- 이 영역은 동적으로 할당되며, 객체가 가비지 컬렉션에 의해 관리됨
- 힙 영역은 Young Generation과 Old Generation으로 나뉘고, Young Generation은 다시 Eden Space, Survivor Space, Old Generation으로 나눠짐
- Young Generation에서 새로운 객체가 생성되고, 객체가 살아남은 경우 Old Generation으로 이동
- 메소드(Method) 영역
- 클래스 정보, 메서드와 필드 정보, 정적 변수(static variables), 상수(final constants), 클래스의 런타임 데이터 등이 저장됨
- 정적 메서드, 정적 변수는 Method Area에 저장됨
- 상수 문자열(String literals)은 String Pool에 저장됨
- PC 레지스터 (Program Counter Register)
- 각 스레드마다 프로그램의 현재 실행 위치를 추적
- Non-Heap
- 힙 영역 외부에서 관리되는 JVM 메모리 영역
- 클래스 메타데이터, 정적 변수 등과 같은 데이터가 저장됨
데이터 타입의 메모리 할당과 성능 영향
- int age = 30;에서 30은 값이고, 이 값은 age라는 변수에 할당됨
- int는 4바이트 크기의 기본 데이터 타입이기 때문에, 30이라는 값을 4바이트 공간에 저장함
- 즉, JVM은 4바이트 크기의 공간을 할당하여 30을 그 공간에 저장하고, age 변수는 스택에 위치하게 됨
byte로 저장하지 않고 int로 저장하는 이유
- int는 4바이트를 차지하는데, 만약 1바이트로 저장하면, 3바이트가 낭비되게 됨
- JVM은 이와 같은 비효율적인 저장 방식 대신 적절한 메모리 크기를 사용하여 성능을 최적화하려 함
- 성능 최적화 과정을 한 번 더 수행하게 되기 때문에 오히려 성능이 안좋아질 수 있음
System.out.println(age)
System 클래스는 JVM이 클래스 로더를 이용해 메모리에 로드됨. 로딩은 System.out.println(age) 실행 시 필요한 클래스를 로드하고 초기화하는 과정. System 클래스의 out, in, err은 각각 4바이트 공간을 할당받고, 기본적으로 null로 초기화됨
finalize()
객체가 가비지 컬렉션 대상이 될 때 호출되는 메서드. 이 메서드를 오버라이딩하면, 객체가 수거되기 전에 특정 작업을 할 수 있음. 그러나 finalize()를 오버라이딩하면, 수거가 늦어질 수 있기 때문에 성능에 영향을 줄 수 있음.
가비지 컬렉션(GC)
가비지 컬렉터는 더 이상 사용되지 않는 **객체(쓰레기 객체)**를 자동으로 제거하고, 메모리 공간을 회수
- 사용되지 않는 객체는 가비지로 간주됨
- 가비지 컬렉터는 기존 메모리 공간에서 유효한 객체들을 다른 공간으로 옮김
- 비어있는 메모리 공간은 모두 정리됨
메서드를 작게 분할해야하는 이유
- 작은 메서드들은 빠르게 실행되고, 메모리에서 할당된 공간을 빠르게 해제함
- 가비지 컬렉터는 메서드 내에서 쓰지 않는 객체들을 찾아서 쓰레기 객체로 간주하고 처리함. 이를 위해 메서드가 빨리 종료되도록 적절하게 메서드를 분할하는 것이 좋음
MyStr name = new MyStr();
- new MyStr()로 생성된 객체는 힙 영역에 할당됨. 만약 MyStr 객체가 char 배열을 포함하고 있다면, 그 char 배열이 힙 영역에 저장됨
- name이라는 참조 변수가 스택 영역에 저장됨. 이 참조 변수는 객체의 메모리 주소를 저장하며, 객체 자체는 힙 영역에 존재함
System.out.println(name)
- name.toString()이 자동 호출되어 객체의 값이 출력됩니다. String 클래스에서는 toString() 메서드가 오버라이드되어 실제 문자열을 반환합니다.
- name.toString()이 자동으로 호출됨. 만약 MyStr 클래스에서 toString()을 오버라이드하지 않으면, 기본적으로 Object 클래스의 toString() 메서드가 호출되어 객체의 해시코드를 출력.
MyStr name = new MyStr("홍길동"); | String name = new String("홍길동"); | |
출력 | 오버라이드 하지 않은 경우 MyStr 객체의 해시코드가 출력 | "홍길동" 출력 |
'[LG 유플러스] 유레카 > Today I Learned' 카테고리의 다른 글
[TIL][02.10] 배열, 상속, 다형성, abstract (0) | 2025.02.10 |
---|---|
[TIL][02.07] String, Method, Arrays (0) | 2025.02.10 |
[TIL][02.05] implements, inheritance, override, abstract, generic, utility (0) | 2025.02.05 |
[TIL][02.04] Array Iteration, Map, TS (0) | 2025.02.04 |
[TIL][02.03] ES6, SSR, CSR, SSG, SOP, CORS (0) | 2025.02.04 |