JiwonDev

2-2 ๋‹ค ์“ด ๊ฐ์ฒด์ฐธ์กฐ๋ฅผ ํ•ด์ œํ•˜๋ผ.

by JiwonDev

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ๊ฒ‰์œผ๋กœ ์ž˜ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š๋Š”๋‹ค.

์˜ค๋ž˜๋œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ฝ”๋“œ๋ฆฌ๋ทฐ, ํž™ํ”„๋กœํŒŒ์ผ๋Ÿฌ ๋ฆฌ๋ทฐ๋ฅผ ํ•˜๋‹ค๋ณด๋ฉด ํ•„์š”์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ˆ˜๋…„๊ฐ„ ์ž ๋ณตํ•˜๋Š” ๊ฒฝ์šฐ๋„ ํ”ํ•˜๋‹ค.

 

# ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค๋ผ๋ฉด ๋ˆ„์ˆ˜์— ์ฃผ์˜ํ•˜๋ผ. (Stack, Array ๋“ฑ)

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์‹ฌํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค. ์–ด๋””์ธ์ง€ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š”๊ฐ€?

// "๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(memory leak)"๊ฐ€ ์–ด๋””์„œ ์ƒ๊ธฐ๋Š”์ง€ ๋ณด์ด๋Š”๊ฐ€?
public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
    elements = new Object[DEFAULT_INITIAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0)
      throw new EmptyStackException();
    return elements[--size];
  }

  if (elements.length == size) // ๊ณต๊ฐ„์ด ๋ถ€์กฑํ•˜๋‹ค๋ฉด ์Šคํƒ ํฌ๊ธฐ๋ฅผ 2๋ฐฐ์”ฉ ๋Š˜๋ฆฐ๋‹ค.
    elements = Arrays.copyOf(elements, 2 * size + 1);
}

 

๋ฐ”๋กœ element[--size] ์ด๋‹ค. ๋”์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฉ”๋ชจ๋ฆฌ์ธ๋ฐ๋„ ์ฐธ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๊ณ ์žˆ๋‹ค.

์ž๋ฐ”์˜ ๊ฐ€๋น„์ง€์ปฌ๋ ‰ํ„ฐ(GC)๋Š” ์ฐธ์กฐ์—ฌ๋ถ€๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ํ•ด์ œํ•œ๋‹ค. ์‚ฌ์šฉํ•˜์ง€๋„ ์•Š๋Š” ๊ณณ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์œ„์˜ ์˜ˆ์ œ๋Š” ๊ทธ๋‚˜๋งˆ ๋ˆ„์ˆ˜๋ฅผ ์ฐพ๊ธฐ ์‰ฌ์šดํŽธ์ด๋‹ค. ์บ์‹œ (key-value)๋‚˜ ๋ฆฌ์Šค๋„ˆ์™€ ์ฝœ๋ฐฑ(callback)์„ ์‚ฌ์šฉํ•  ๋•Œ ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋”์ฐํ•˜๋‹ค.

map.put(key1, "test a");
map.put(key2, "test b");

key1 = null; 
/* ... ๋‹ค๋ฅธ ์ฝ”๋“œ ... */

// ์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์ง€๋งŒ map์— ์žˆ๋Š” key1์˜ ๋ฐ์ดํ„ฐ์˜ ์ฐธ์กฐ๋Š” ๊ณ„์† ๋‚จ์•„์žˆ๋‹ค.

 

๋˜ํ•œ JVM์˜ GC๊ฐ€ ์ด๋Ÿฌํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฐพ์•„์„œ ํ• ๋‹น ํ•ด์ œํ•ด์ค€๋‹ค๊ณ  ํ•œ๋“ค, ํฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

GC์•ˆ์—๋Š” ํฌ๊ฒŒ Young ์˜์—ญ๊ณผ Old ์˜์—ญ์ด ์กด์žฌํ•˜๋Š”๋ฐ, ์ด๋Ÿฐ์‹์œผ๋กœ ๊ณ„์†ํ•ด์„œ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ๋Š” Old ์˜์—ญ๊นŒ์ง€ ์˜ฎ๊ฒจ์ง€๊ฒŒ ๋˜๊ณ  Old ์˜์—ญ์— ์Œ“์ธ ๋ฉ”๋ชจ๋ฆฌ๋Š” ํ”„๋กœ๊ทธ๋žจ ๊ตฌ๋™์„ ์ž ๊น ๋ฉˆ์ถฐ๋ฒ„๋ฆฌ๊ณ  (Stop-the-World) ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ํ•ด์ œํ•œ๋‹ค.

 

(G1GC ์ด์ „์—๋Š”) ์„œ๋ฒ„์ปดํ“จํ„ฐ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์›Œ๋‚™์ปค์„œ, Old ์˜์—ญ์— ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์Œ“์ด๊ฒŒ๋œ๋‹ค๋ฉด JVM์˜ GC๊ฐ€ Old ์˜์—ญ์„ ๋น„์šฐ๊ธฐ ์œ„ํ•ด์„œ ์„œ๋ฒ„๊ฐ€ 2~3๋ถ„๊ฐ„ ๋ฉˆ์ถฐ๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค. ์žฅ์• ๊ฐ€ ๋‚œ๊ฒƒ๋„, ๋กœ์ง์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ๋„ ์•„๋‹Œ๋ฐ ๋ง์ด๋‹ค. 

 

 

# ํ•ด๊ฒฐ์ฑ…

๊ฐ„๋‹จํ•˜๋‹ค. ๋‹ค์“ด ๊ฐ์ฒด๋Š” ์ฐธ์กฐ๋ฅผ ํ•ด์ œํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

public Object pop() {
  if (size == 0)
    throw new EmptyStackException();
  Object results = elements[--size];
  elements[size] = null;  // ๋งŒ๊ธฐ  ์ฐธ์กฐ ์ œ๊ฑฐ
  return results;
}

 

๋‹ค๋งŒ ์œ„์™€ ๊ฐ™์ด null์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด NullPointException ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋˜ ์ƒ๊ฒจ๋ฒ„๋ฆฐ๋‹ค.

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ž๋ฐ”์—๋Š” Weak ์ž๋ฃŒํ˜• (WeakHashMap)๋“ฑ์„ ์ œ๊ณตํ•ด์ค€๋‹ค. ๊ธฐ์กด์˜ ์ž๋ฃŒํ˜•๊ณผ ์‚ฌ์šฉ๋ฒ•์€ ๋™์ผํ•˜๋‚˜ ๊ฐ’์„ ์ง์ ‘ ์ฐธ์กฐํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์ฐธ์กฐ ๊ฐ์ฒด(WeakReference)๋ฅผ ํ†ตํ•ด ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ ํ•ด๋‹น ๊ฐ’์ด ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํŒ๋‹จํ•˜๋ฉด (prime == null์ด ๋œ๋‹ค๋ฉด) ํ•ด๋‹น ์›์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  GCํ•ด๋ฒ„๋ฆฐ๋‹ค. 

 

  • Weak ์ž๋ฃŒํ˜•์€ ๊ธฐ์กด์˜ ์ž๋ฃŒํ˜•๊ณผ ๋˜‘๊ฐ™์ด ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ์— ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.
  • Weak ์ž๋ฃŒํ˜•์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ , Key๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•ด๋ฒ„๋ฆฌ๋ฉด ์•„๋ฌด๋Ÿฐ ์˜๋ฏธ๊ฐ€ ์—†์–ด์ง„๋‹ค. WeakReference๋กœ ์ฐธ์กฐํ•ด์•ผํ•œ๋‹ค.
public class WeakHashMapTest {
 
    public static void main(String[] args) {
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
 
        Integer key1 = 1000;
        Integer key2 = 2000;
 
        map.put(key1, "test a");
        map.put(key2, "test b");
 
        key1 = null;
        // map์—์„œ๋Š” ์‚ญ์ œ๋œ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜์–ด์žˆ๋‹ค.
        // ํ•˜์ง€๋งŒ WeakReference ์˜ prime = null์ด ๋™์ž‘๋œ๋‹ค.
        // ์ฆ‰ GC๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ•ด๋‹น ์›์†Œ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์‚ญ์ œ๋œ๋‹ค.
 
    }
}

 

 

# ๊ฐ์ฒด์˜ ๊ธฐ๋ณธ ์ข…๋ฃŒ์ž (finalizer, Cleaner)๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ๋ผ

Java 9 ์ดํ›„ ์—†์–ด์ง„ ๋ฉ”์„œ๋“œ๋ผ ๋ชจ๋ฅผ ์ˆ˜ ์žˆ์ง€๋งŒ ์ž๋ฐ”์˜ Object ๊ฐ์ฒด์—๋Š” finalizer()๊ฐ€ ์กด์žฌํ–ˆ์—ˆ๋‹ค. 

์ด๋ฅผ ํ˜ธ์ถœํ–ˆ๋‹ค๊ณ  ํ•ด์„œ ๋ฐ”๋กœ ์‚ญ์ œ๋˜๋Š” ๊ฒƒ๋„์•„๋‹ˆ๊ณ , Lock์ด ๊ฑธ๋ ค ํ”„๋กœ๊ทธ๋žจ ์ „์ฒด๊ฐ€ ๋ธ”๋Ÿญ๋  ์ˆ˜ ์žˆ๋Š” ์น˜๋ช…์ ์ธ ๋ฉ”์„œ๋“œ์˜€๋‹ค.

@Override
public void finalize() {
    // ...
}

 

Java 9 ์ดํ›„์—๋Š” Cleaner ๊ฐ€ ์†Œ๋ฉธ์ž๋กœ ๋„์ž…๋˜์—ˆ์œผ๋ฉฐ, ๋ณดํ†ต AutoCloseable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ๋“ฑ๋ก๋œ ์Šค๋ ˆ๋“œ์— ์ •์˜๋œ ํด๋ฆฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.  ์ด๋ฅผ ํ†ตํ•ด try-with-resource ๋ฌธ์˜ ๋™์ž‘์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

try(Worker worker = new Worker()) {
    worker.work();
} catch(...) {

}
public class CleaningRequiredObject implements AutoCloseable {

    private static final Cleaner cleaner = Cleaner.createโ€‹();

    private static class CleanData implements Runnable {

        @Override
        public void run() {
            // ์—ฌ๊ธฐ์„œ ํด๋ฆฐ ์ž‘์—… ์ˆ˜ํ–‰
        }
    }

    private final CleanData;
    private final Cleaner.Cleanable cleanable

    public CleaningRequiredObject() {
        this.cleanData = new CleanData();

        // cleanable ๋“ฑ๋ก
        this.cleanable = cleaner.register(this, state);
    }

    @Override
    public void close() {
        cleanable.clean();
    }
}

 

ํ•˜์ง€๋งŒ DB Connector๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ์ˆ˜์ค€์ด ์•„๋‹ˆ๋ผ๋ฉด, ๋ณดํ†ต์€ ์‚ฌ์šฉํ•  ์ผ์ด ๊ฑฐ์˜ ์—†๋‹ค.

Java9์˜ Cleaner๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•œ๋“ค ์„ฑ๋Šฅ์— ๋ฌธ์ œ๊ฐ€ ๋งŽ๊ณ , ๊ฐ์ฒด ์ง๋ ฌํ™”(Serialize)๋ฅผ ํ†ตํ•œ ๋ณด์•ˆ์ ์ธ ๋ฌธ์ œ์™€ ์ˆ˜ํ–‰ ์‹œ์ ์„ ๋ณด์žฅํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฑด ์—ฌ์ „ํžˆ ๋˜‘๊ฐ™๋‹ค. ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ (๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ - JNI๋ฅผ ์ง์ ‘ ๊ตฌํ˜„)๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๋ฉ”์„œ๋“œ์ด๋‹ค.

 

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ