티스토리 뷰

https://medium.com/javarevisited/basic-memory-saving-techniques-for-java-programming-6677a7237a69

 

위 글을 번역하면서, 조금씩 내용을 추가했다.

 

1. 프리미티브 타입(primitive type)을 사용하자

Integer x = 42; => int x = 42;
Double d = 3.14; => double d = 3.14;
Boolean b = true; => boolean b = true;

Integer, Double, Boolean과 같은 프리미티프 타입을 Wrapping한 Object들을 사용하면 불필요한 오버헤드를 만들 수 있으니, 사용을 피하는게 좋다.

 

2. 스트링을 결합할 때 StringBuilder를 사용하자

String s = "Hello" + " World";

대신에 아래를 권장한다.

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
String s = sb.toString();

사실 하나만 결합할 경우 큰 차이를 못 느낄 것이다. 하지만 내부 동작 과정을 확인하고, 다중으로 도는 것을 보면 왜 그런지 알 수 있을 것이다.

String a = "";

for(int i = 0; i < 10000; i++) {
    a = a + i;
}

--> 위와 같이 작성하면, 아래와 같이 동작한다.

String b = "";

for(int i = 0; i < 10000; i++) {
    b = new StringBuilder(b).append(i).toString();
}

StringBuilder를 쓰라는 이유가 있다.

 

이것도 자바 9버전부터 컴파일러가 자동으로 변환해주게 변경된 거라 9버전 이하의 자바를 사용 중이라면 성능 차이는 더 커질 것이다.

 

StringBuilder로 구현하면 차이를 확실히 알 수 있다.

final StringBuilder a = new StringBuilder();

for(int i = 0; i < 10000; i++) {
    a.append(i);
}

final String b = a.toString();

훨씬 간결하게 정리된다.

 

3. lazy 초기화를 사용하자

이건 나도 처음 듣는 방법인데, 생각보다 괜찮은 방식인 것 같다.

private List<String> myList;
public List<String> getMyList() {
    if (myList == null) {
        myList = new ArrayList<>();
    }
    return myList;
}

클래스가 로드될 때 목록 개체를 초기화하는 대신 지연 초기화를 사용하여 실제로 필요할 때까지 개체 생성이 지연된다.

이렇게 하면 불필요한 개체 생성을 방지하여 메모리를 절약할 수 있다고 한다.

 

 

4. 배열의 크기가 명확할 때는 초기화해서 사용하자

ArrayList도 좋은 컬렉션이지만, 문자열의 크기를 알고 있을 때는 Array를 사용하는게 좋다고 한다.

String[] array = new String[1000];

 

5. Object를 재사용하자

List<String> tempList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    tempList.add("Item " + i);
    // do something with tempList
}
tempList.clear(); // clear the list for reuse
for (int i = 0; i < 1000; i++) {
    tempList.add("Another item " + i);
    // do something with tempList
}

좀 귀찮은 방법이긴 하지만, 리스트를 새로 만들지 않고 재사용하면 메모리 절약을 할 수 있다.

 

6. 정적 팩토리 매서드를 사용하자

public static List<String> getList() {
    return new ArrayList<>(1000); // specify the initial capacity
}

매번 새로운 ArrayList를 만드는 것보다 특정한 크기의 ArrayList를 사용하는 것이 메모리 절약에 도움을 준다고 한다.

 

7. flyweight 패턴을 사용하자

Map<String, String> map = new HashMap<>();
String key = "myKey";
String value = "myValue";
map.put(key, value);
String anotherValue = map.get(key); // reuse the same object instead of creating a new one

String 에 새 문자열을 할당하는 것보다 기존에 사용하던 객체를 공유하는 것이 메모리 절약에 도움을 준다고 한다.

 

8. intern() 매서드를 사용하자

String s1 = "hello";
String s2 = "hello". Intern();

intern()을 사용하면 동일한 문자열이 여러 번 생성되더라도 문자열 인스턴스 하나만 생성되도록 한다. 이렇게 하면 생성되는 문자열 객체의 수를 줄여 메모리를 절약할 수 있다.

 

9. 불필요한 autoboxing을 피하자

int a = 10;
Integer b = a; // 오토박싱
int c = b; // 오토언박싱

위의 경우가 오토박싱/언박싱이다. 이렇게 쓰지말고, 아래와 같이 valueOf 매서드를 쓰면 메모리를 절약할 수 있다고 한다.

int i = 42;
Integer j = Integer.valueOf(i); // use Integer.valueOf() instead of autoboxing

 

10. finalize() 매서드를 주의해서 사용하자

public class MyClass {
    private byte[] data = new byte[1000];
    
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        // clean up resources
    }
}

finalize() 메서드는 호출되는 시점이 보장되지 않으며, 실행하는데 있어서 큰 오버헤드가 발생한다. 또한 finalize() 메서드가 실행되는 동안에는 객체가 다른 스레드에서도 사용할 수 없으므로 동시성 문제가 발생할 수 있다.

 

Java 9에서는 finalize() 메서드가 deprecated되었고, Java 11부터는 삭제되었으니, 이전 버전의 자바를 사용 중이라면 꼭 주의해야한다.

 

마치며

사실 메모리를 신경쓰면서 개발할거면, 개발 언어로 자바는 적합하지 않다고 생각한다. 메모리를 신경쓰면서 개발하기 좋은 언어가 요즘엔 정말 많기 때문이다.

 

하지만 빡빡한 상황에서 어쩔 수 없이 자바를 사용 중이라면, 위 내용이 제법 도움이 될 것 같다.


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함