JAVA

[Java] JVM λ™μ‹œμ„± μ œμ–΄ 원리 (Implicit Lock)

YeopJu 2024. 8. 27. 19:25
λ°˜μ‘ν˜•

μ•žμ„  κΈ€μ—μ„œ μžλ°” λ™μ‹œμ„± μ œμ–΄ 방식인 `synchronized`, `ReentrantLock`, `Semaphore`에 λŒ€ν•΄ μ•Œμ•„λ΄€λ‹€. 이 쀑 `ReentrantLock`, `Semaphore` 은 μžλ°”μ—μ„œ μ œκ³΅ν•˜λŠ” κ³ μˆ˜μ€€ 락 (Explicit Lock)이며 OS μˆ˜μ€€μ˜ λͺ¨λ‹ˆν„° λ½κ³ΌλŠ” λ¬΄κ΄€ν•˜κ²Œ 자체적인 λŒ€κΈ° 큐와 락 νšλ“ λ©”μ»€λ‹ˆμ¦˜μ„ μ‚¬μš©ν•˜μ—¬ λ™μž‘ν•œλ‹€.

 

λ°˜λ©΄μ— `synchronized` λŠ” 객체가 κ°€μ§€κ³  μžˆλŠ” 헀더λ₯Ό μ‚¬μš©ν•˜μ—¬ μ œμ–΄ν•˜λ©° JVMμ—μ„œ κ΄€λ¦¬ν•˜λŠ” JVM λ‚΄μž₯ 락, 즉 μ €μˆ˜μ€€ 락 (Implicit Lock) 이닀.`synchronized`λŠ” JVM λͺ¨λ‹ˆν„° 락을 기반으둜 μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•΄ `Biased Locking -> Lightweight Locking -> Heavyweight Locking`을 λ‹¨κ³„μ μœΌλ‘œ μ μš©ν•œλ‹€. 이와 같이 경쟁 상황에 따라 κ°€λ²Όμš΄ 락 λ°©μ‹μ—μ„œ λͺ¨λ‹ˆν„° λ°©μ‹μœΌλ‘œ μ μ§„μ μœΌλ‘œ μ „ν™˜λ˜λŠ” 과정을 이번 κΈ€μ—μ„œ μžμ„Έν•˜κ²Œ λ‹€λ£° 것이닀.

 

 


 

β˜‘οΈ 곡유 객체

 

κ°μ²΄λŠ” ν•œμ •λœ μžμ›μœΌλ‘œ, μƒμ„±λ˜λ©΄ Heap μ˜μ—­μ— μ €μž₯λœλ‹€. λ˜ν•œ μŠ€λ ˆλ“œλŠ” Stack μ˜μ—­μ— μƒμ„±λ˜λŠ”λ°, ν•΄λ‹Ή μŠ€λ ˆλ“œλ“€μ€ 객체λ₯Ό μ°Έμ‘°ν•˜κ²Œ 되고 이 κ³Όμ •μ—μ„œ 두 개 μ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ ν•˜λ‚˜μ˜ 동일 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” 일이 λ°œμƒν•˜κ²Œ λœλ‹€. μ΄λ•Œ ν•„μš”ν•œ κ°œλ…μ΄ 동기화이닀. 동기화λ₯Ό μœ„ν•΄μ„œλŠ” Lock이 ν•„μš”ν•œλ° μ΄λŠ” 객체의 Object 헀더 뢀뢄에 ν¬ν•¨λ˜μ–΄ μžˆλ‹€. 객체 해더 쀑 ν•΄λ‹Ή 객체의 μ—¬λŸ¬κ°€μ§€ 메타데이터(락 μƒνƒœ, GC 정보 λ“±)λ₯Ό 확인할 수 μžˆλŠ” 뢀뢄을 Mark Word라고 ν•œλ‹€. 

 

 

μœ„ 사진 μš°μΈ‘μ— Mark WordλŠ” UnLock μƒνƒœμ΄λ©° 이 λ–„ Hash CodeλŠ” 객체의 고유번호λ₯Ό μ˜λ―Έν•œλ‹€. 이 μ™Έμ˜ λΉ„νŠΈλ“€μ€ 경쟁 μƒνƒœμ— 따라 λ³€κ²½λ˜λŠ”λ° μ΄λŠ” μ•„λž˜μ—μ„œ 더 μžμ„Έν•˜κ²Œ μ‚΄νŽ΄λ³Ό μ˜ˆμ •μ΄λ‹€.

 

 

1️⃣ Biased Locking

μ„œλΉ„μŠ€ μœ ν˜•μ— 따라 λ‹€λ₯΄κ² μ§€λ§Œ, 일반적으둜 객체에 λŒ€ν•΄μ„œ 경쟁이 μΌμ–΄λ‚˜μ§€ μ•ŠλŠ” κ²½μš°κ°€ 많으며, 경쟁이 μΌμ–΄λ‚˜λ”λΌλ„ νŠΉμ • 객체에 λ™μΌν•œ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œκ°€ κ³„μ†ν•΄μ„œ μ ‘κ·Όν•˜λŠ” κ²½μš°κ°€ λŒ€λΆ€λΆ„μ΄λ‹€. 이 λ•Œ λ™μΌν•œ μŠ€λ ˆλ“œμ— λŒ€ν•œ 반볡적인 동기화 μž‘μ—…μ€ CPU의 λ‚­λΉ„λ‘œ 이어지기 λ•Œλ¬Έμ— Mark Wordλ₯Ό νŠΉμ • μŠ€λ ˆλ“œ μ „μš© Mark Word둜 λ³€ν™˜ν•˜λŠ”λ° 이λ₯Ό Biased Locking이라고 ν•œλ‹€.

 

 

 

 

κ·Έλ¦Όκ³Ό 같이 Hash Code뢀뢄은 μ ‘κ·Όν•˜λŠ” μŠ€λ ˆλ“œμ˜ ID둜, Biased의 λΉ„νŠΈλŠ” 1둜 λ°”λ€Œκ²Œ λœλ‹€.

이후에 λ™μΌν•œ μŠ€λ ˆλ“œκ°€ ν•΄λ‹Ή 객체에 λ‹€μ‹œ ν•œλ²ˆ μ ‘κ·Όν•œλ‹€λ©΄ μŠ€λ ˆλ“œ ID κ°’μ˜ 비ꡐλ₯Ό 톡해 μ‰½κ²Œ Lock을 흭득할 수 μžˆλŠ” 것이닀.

 

 

 

 

2️⃣ Lightweight Locking

μ—¬μ „νžˆ 경쟁이 μ‹¬ν•˜μ§€ μ•Šμ„ λ•Œ μ‚¬μš©λ˜λŠ” 락 λ°©μ‹μœΌλ‘œ, 곡유 객체에 λŒ€ν•œ 경쟁이 μ‹œμž‘λ˜μ—ˆμ„ λ•Œ Biased Locking이 ν•΄μ œλ˜κ³  Lightweight Locking이 μ μš©λœλ‹€. μ΄λ•Œ 객체의 락을 ν­λ“ν•œ μŠ€λ ˆλ“œλŠ” 객체의 Mark Word와 객체의 μ£Όμ†Œλ₯Ό μžμ‹ μ˜ μŠ€νƒμ˜μ—­μ— μ €μž₯ν•΄λ†“λŠ”λ‹€. λ˜ν•œ 객체의 Mark WordλŠ” Lock Record Address둜 λ³€ν™˜λ˜λ©° μ΄λŠ” Lock Recordκ°€ μ €μž₯된 μŠ€λ ˆλ“œμ˜ μŠ€νƒ μ˜μ—­μ„ 가리킨닀. 이렇듯 μ„œλ‘œλ₯Ό ν¬μΈνŒ…ν•˜κ³  μžˆλŠ” μƒνƒœκ°€ 되면 Lock을 ν­λ“ν•œ 것이닀,

 

 

 

Lightweight Locking μƒνƒœμ—μ„œ 락을 ν­λ“ν•˜μ§€ λͺ»ν•œ μŠ€λ ˆλ“œλŠ” Spin Lock을 돌게 되며, 이λ₯Ό 톡해 μ»¨νƒμŠ€νŠΈ μŠ€μœ„μΉ˜ λΉ„μš©μ„ μ ˆκ°ν•  수 μžˆλ‹€. 이 λ•Œ 둜우 μ½”λ“œ μˆ˜μ€€μ—μ„œμ˜ μƒν˜Έλ°°μ œλ₯Ό μœ„ν•΄ CPU μˆ˜μ€€μ˜ CAS 연산이 μΌμ–΄λ‚˜κ²Œ 되며 이와 같은 μ›λ¦¬λ‘œ μ›μžμ μΈ 연산을 μ‚¬μš©μž μˆ˜μ€€μ—μ„œ ν•  수 μžˆλ„λ‘ κ΅¬ν˜„ν•΄λ†“μ€ 것이 `cocurrent.atomic` νŒ¨ν‚€μ§€μ΄λ‹€.

 

 

 

3️⃣ Heavyweight Locking

 

μŠ€λ ˆλ“œ κ°„μ˜ 경쟁이 μ‹¬ν™”λ˜λ©΄ Lightweight Locking은 Heavyweight Locking으둜 λ³€ν™˜λ˜λ©° 이 λ•ŒλΆ€ν„° λͺ¨λ‹ˆν„° λ°©μ‹μ˜ μƒν˜Έλ°°μ œκ°€ μ‚¬μš©λœλ‹€. μŠ€λ ˆλ“œκ°€ 락 흭득을 μœ„ν•΄ λŒ€κΈ°ν•  λ–„ λΈ”λ‘œν‚Ήλ˜μ–΄, 락을 ν•΄μ œν•  λ•ŒκΉŒμ§€ CPUλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  λŒ€κΈ°ν•œλ‹€. λ˜ν•œ OS μˆ˜μ€€μ˜ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­μ΄ μΌμ–΄λ‚˜κΈ° λ•Œλ¬Έμ— 비ꡐ적 큰 λΉ„μš©μ΄ λ°œμƒν•œλ‹€. 

 

 

 

μœ„ 사진과 같은 λͺ¨λ‹ˆν„° 방식을 Non-blocking Monitor 방식이라고 ν•œλ‹€. μŠ€λ ˆλ“œλŠ” λͺ¨λ‹ˆν„°μ— μ§„μž…ν•˜κΈ° μœ„ν•΄ Wait Setκ³Ό Entry Setμ—μ„œ λŒ€κΈ°ν•œλ‹€. Wait Set은 쑰건 λ³€μˆ˜μ™€ ν•¨κ»˜ μ‚¬μš©λ˜μ–΄ νŠΉμ • 쑰건을 λ§Œμ‘±ν•  λ•ŒκΉŒμ§€ μŠ€λ ˆλ“œκ°€ λŒ€κΈ°ν•˜λŠ” μž₯μ†Œμ΄λ©°, λ‹€λ₯Έ μŠ€λ ˆλ“œμ— μ˜ν•΄ κΉ¨μ–΄λ‚˜κ²Œ 되면 Entry Set으둜 μ΄λ™ν•˜μ—¬ 락을 흭득할 수 μžˆλŠ” μƒνƒœκ°€ λœλ‹€. λ˜ν•œ Entry Set은 락을 ν­λ“ν•˜κΈ° μœ„ν•΄ λŒ€κΈ°ν•˜λŠ” μž₯μ†Œλ‘œ, 락이 λ°˜λ‚©λ˜λ©΄ Entry Set에 μžˆλŠ” μŠ€λ ˆλ“œ 쀑 ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œκ°€ 락을 ν­λ“ν•˜λ©° μž„κ³„μ˜μ—­μœΌλ‘œ μ§„μž…ν•˜κ²Œ λœλ‹€.

 

ν•˜μ§€λ§Œ μœ„μ™€ 같은 방식은 Entry Set에 μžˆλŠ” μŠ€λ ˆλ“œλ“€μ— μš°μ„ μˆœμœ„κ°€ λΆ€μ—¬λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— Entry Set λ‚΄μ—μ„œ μ˜€λž«λ™μ•ˆ λŒ€κΈ°ν•˜λŠ” μŠ€λ ˆλ“œκ°€ μƒκΈ°λŠ” 곡평성 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€. 이 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλŠ” 방법이 μžλ°” ν‘œμ€€ APIμ—μ„œ μ œκ³΅ν•˜λŠ” `java.util.concurrent.locks` νŒ¨ν‚€μ§€μ˜ Explicit Lock이닀.

 

 


 

 

β˜‘οΈ κ²°λ‘ 

JVM λ‚΄λΆ€μ μœΌλ‘œ κ²½μŸμƒνƒœμ— 따라 락의 방식을 λ‹¨κ³„μ μœΌλ‘œ λ³€κ²½ν•˜μ—¬ μ„±λŠ₯ ν–₯상이 κ°€λŠ₯토둝 ν–ˆλ‹€.

ν•˜μ§€λ§Œ synchronizedλŠ” 단일 JVM ν™˜κ²½μ—μ„œλŠ” μœ νš¨ν•˜μ§€λ§Œ ν˜„λŒ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ 닀쀑 μ„œλ²„λ‚˜ 닀쀑 JVMμ—μ„œ κ΅¬λ™λ˜λŠ” κ²½μš°κ°€ 많기 λ•Œλ¬Έμ— λΆ„μ‚° ν™˜κ²½μ—μ„œ μ ν•©ν•˜μ§€ μ•Šμ„ 수 μžˆλ‹€. λ”°λΌμ„œ μ„±λŠ₯적 μš”κ΅¬μ‚¬ν•­μ„ μΆ©μ‘±μ‹œν‚€κΈ° μœ„ν•΄μ„œλŠ” 락 μ˜€λ²„ν—€λ“œλ₯Ό 쀄일 수 μžˆλŠ” ReentrantLock, Atomic 클래슀λ₯Ό,  λΆ„μ‚° μ‹œμŠ€ν…œμ„ μ§€μ›ν•˜κΈ° μœ„ν•΄μ„œλŠ” Redis, Zookeeper와 같은 뢄산락 λ™μ‹œμ„± μ œμ–΄ 도ꡬ듀을 μ‚¬μš©ν•΄μ•Όν•œλ‹€.

 

 

 

λ°˜μ‘ν˜•