return true
2k
2016-04-20 13:20:28
27
7254

이 방식이 무리가 많이 가는 코딩방식일까요?


언어는 자바구요

for 문을 돌릴때마다 String 객체를 생성해서 할당하는 코딩법이 너무나도 많이 보입니다.

String 뿐만 아니라 다른 객체들도 종종 보입니다.


예를들면

for(int i=0; i<length;; i++) {

  String a = object.arr1[i];

  String b = object.arr2[i];

  String c = object.arr3[i];
  
  ....
}

이런 방식이요....

for문을 돌때마다 객체를 생성합니다..


그런데 이게 맞는거겠죠?

String a;
String b;
String c;
for(int i=0; i<length; i++) {
  a = object.arr1[i];
  b = object.arr2[i];
  c = object.arr3[i];

...
}


첫번째 방법으로 했을 때 for문의 length가 아무리 커도 메모리문제 등에 무리가 없을까요?


1
1
  • 답변 27

  • 여미융
    62
    2016-04-20 13:36:43

    매번 메모리를 할당하는거랑 할당된 메모리에 값을 넣어주는거랑 차이가 좀 크지않을까용???


    별 차이없으려나 ㅎ;

    0
  • 석두개발
    2016-04-20 13:39:09

    위에 것이 메모리 부분에서는 더 적당하지 않나 생각하는데요.


    아래 것은 값이 length 가 0 이어도 메모리 값을 가지고 있게 되지만 위에 것은 값이 0 이면 아예 객체 선언 자체가 안되기 때문에 메모리 면에서는 더 나은 것으로 생각됩니다.

    0
  • return true
    2k
    2016-04-20 13:40:54

    length가 0이 될 일은 없고 대부분 수십에서 수백, 많으면 수천 int 값이 할당된다면요?

    0
  • asd
    16k
    2016-04-20 13:42:20

    잘 생각해보면

    첫번째 방법이 왜 메모리에 문제가 있다고 생각하는가?

    에 대해 생각해보시기바랍니다.

    Abc abc = new Abc(); 에서 메모리에 할당되는 주체는 new Abc(); 이지 Abc abc가 아닙니다.


    for(int i = 0; i < 100; i++) Abc abc = new Abc();
    
    Abc abc
    
    for(int i = 0; i < 100; i++) abc = new Abc();


    둘다 Abc의 인스턴스를 100개를 생성합니다.

    abc는 변수로서 단지 객체를 가리키는 레퍼런스를 갖고있을뿐입니다.

    즉 사실상 첫번쨰나 두번째나 생성되는 객체는 동일합니다(차이가 있다고해도 변수생성 정도의 미미한정도).

    따라서 두가지 방법에서 메모리를 비교하는건 거의 무의미하고 저럴때는 사실상 for문 바깥에서도 변수의 스코프가 유지되는 두번째보단 for문 내에서 스코프가 종료되는 첫번째를 권장합니다.

    3
  • 정욱이
    235
    2016-04-20 13:44:56

    생각해보니 new가 아니었군요... 

    String을 for문 내에서 사용하느냐 밖에서 사용하느냐에따라의 코드 스타일인거같습니다.

    0
  • return true
    2k
    2016-04-20 13:45:50

    LichKing님 그렇군요

    자바 공부할때 몇번이나 상기시켰던 내용들인데 잊고 있었습니다.

    역시 기본기가 중요하네요.

    다시한번 봐야겠습니다.

    답변 감사합니다.!

    0
  • taskkill
    38
    2016-04-20 13:50:13

    제생각에는 지역변수라 상관없지않나싶습니다 

    0
  • asd
    16k
    2016-04-20 13:54:19

    http://stackoverflow.com/questions/8803674/declaring-variables-inside-or-outside-of-a-loop

    참고해보세요.

    1
  • 금꽃
    161
    2016-04-20 13:58:22

    제가 일하면서 배웠을 때는 위에처럼 반복문 안에서 변수를 선언해서 만드는것은 좋지 않다고 배웠었습니다. 그 이유가 반복문을 돌면서 계속 String a 라는 변수를 새로 만들어주는데

    이게 i 가 0일때 만들어지는 String a 하고 i가 2일때 만들어지는 String a 하고 이름만 똑같고

    다른 변수이기 때문에 반복문이 돌때마다 계속 변수를 새로 생성시키는 거라고 들었어서 좋지 않다고 들었었습니다.

    아래처럼 해줘야지 좋은거라고 알고있었는데 아닌가요?

    0
  • 여미융
    62
    2016-04-20 14:03:52

    저도 잘 이해가 안되네요 ㅠ..


    얼마전에 for문을 두개 써서 한번 for문을 돌면 몇만번 이상이 되는 연산이있었는데...위의 코딩방식들이 속도 차이가 나던것 같던데 ㅠㅠ;;;제가 잘못알고있는건가요.


    물론 디버깅 환경이였습니다 ㅎ;;;


    릴리즈로하면 어떻게 될지ㅠ

    0
  • 금꽃
    161
    2016-04-20 14:08:44

    저는 신입때 코딩해놓은거 팀장님이 소스 하나씩 보시면서 안좋은습관 고치라고 했던것중 하나가

    반복문 안에서 변수선언 하는거였습니다.

    그리고 또 하나가

    for(int i = 0; i < length; i++){

      //어쩌구저쩌구

    }

    for(int i = 0; i < length; i++){

    //2번째 어쩌구저쩌구

    }

    뭐 이런식으로 반복문 반복문이 이어서 나올때도 for안에 i라는 변수를 안에서 선언해서 쓰지말고

    int i = 0;

    for(i = 0; i < length; i++){

      //어쩌구저쩌구

    }

    for(i = 0; i < length; i++){

    //2번째 어쩌구저쩌구

    }

    뭐 이런식으로 해야지 좋은거다 이런 얘기를 들었었는데

    이것도 뭐가 더 좋으건지 궁금하네요

    0
  • 여미융
    62
    2016-04-20 14:30:05

    방금 그냥 스트링으로만 테스트 해봤는데 차이가 크게 없네요


    생성해야하는 객체가 작아서 그런가;;;전엔 차이가 컷던거같은뎅


    좀더 공부를해봐야겠네요^^


    ----추가----

    new가 아니기 때문에란 말에서 어느정도 이해가간것같습니다 ㅎ..


    어렵네요 ㅠ

    0
  • asd
    16k
    2016-04-20 14:40:13

    실제 성능에 얼마나 차이가있는지 솔직히 거기까진 제가 모르니 뭐라 말할수가없지만 한가지만 짚고넘어가자면

    객체 생성은 new에서 하는거지 변수선언을 안에서하고 밖에서하는거에 차이가 있는건 아닙니다. new냐 아니냐 가 왜 갈리는지 저는 잘 이해가 안가네요. new로 생성하든 팩토리메서드를 사용하든 어쨋든 객체를 받는거고 변수는 해당 객체의 레퍼런스를 가리키는것일뿐입니다.

    지금 이 질문의 핵심은 '변수선언을 어디서 하느냐' 인거지 '객체생성을 어디서 하느냐'가 아닙니다. 첫번째든 두번째든 객체생성은 반복문안에서 이루어지는거에요.


    일단 제가 위에 첨부한 스택오버플로우 링크를 살펴보면 '바이트코드는 변수를 안에선언하나 밖에선언하나 동일하다. 그런데 스코프를 적게가져가야하니까 안에 선언하자' 라고 나와있고요.


    굳이 성능에 문제가있다고 생각한다면 반복되는 변수선언일뿐인데 객체생성도 아닌 변수선언이 눈에띄게 성능에 영향을 줄지는 잘 모르겠네요.

    1
  • return true
    2k
    2016-04-20 14:40:23

    1. 밖에 선언

    Object a;
    
    for(~~~) {
    
    a = new Object();
    
    }


    2. 안에 선언

    for(~~~) {
    
    Object a = new Object();
    
    }


    어짜피 둘다 매번 new로 객체가 생성되므로 똑같다는 말이죠

    객체 생성이 아닌 그냥 값만 할당할때에도 마찬가지로 똑같구요


    그리고 안에 변수를 선언하는게 변수 scope를 for문 안으로만 한정지으니 오히려 더 좋은 방법이라는 뜻으로 이해를 했습니다.

    0
  • 박근혜
    4k
    2016-04-20 14:47:15

    전자로 만드는 것이 정답입니다..후자는 쓸때없이 코드가 길어져서 가독성이 떨어집니다  


    0
  • return true
    2k
    2016-04-20 14:53:10

    밖에다 선언하는게 좋은 방법이라고 배우신 분들은 아마도

    밖에 한번만 Object obj = new Object(); 이렇게 객체를 생성하고 for문 안에서 obj를 사용하면 되는데

    for문 상단에다가 new Object()를 놓아버려서 매번 생성되는 그런 부분을 경계하라는 뜻이 아니었겠나 예상해봅니다.


    즉, 밖에서 한번만 만들어서 사용하면 될 것을 반복문 안에서 매번 만들 필요는 없다는 가르침이었겠죠

    예를 들면 PreparedStatement 같은게 있겠네요

    0
  • 제타건담
    6k
    2016-04-20 15:09:57

    첫번째나 두번째나 new는 아니기 때문에 문제는 아니지만..만약 두번째가 new로 하는거라면 이거는 치명적입니다..

    빈번하게 GC가 일어날 소지가 있죠..

    예를 들면..루프를 3000번 돈다고 가정하고 이에 대한 메모리 할당을 놓고 보면..

    첫번째 loop

    String a = 0x0001

    String b = 0x0002

    String c = 0x0003

    두번째 loop

    String a = 0x0004     String a = 0x0001(GC 예약)

    String b = 0x0005     String a = 0x0002(GC 예약)

    String c = 0x0006     String a = 0x0003(GC 예약)

    세번째 loop

    String a = 0x0007      String a = 0x0004(GC 예약) String a = 0x0001(GC 예약)

    String b = 0x0008      String b = 0x0005(GC 예약) String b = 0x0002(GC 예약)

    String c = 0x0009      String c = 0x0006(GC 예약) String c = 0x0003(GC 예약)

    ..

    이런식으로 3000번을 다 돌면..실제로는 3개만 사용하고 나머지 8997개(3000 * 3 - 3)는 GC로 예약되어 있는거죠..

    그리고 GC가 되어야 이 잡혀있는 메모리가 해제가 되어 다른 변수가 할당받아 사용할 수 있는겁니다..

    즉 loop 안에 반복적으로 new로 할당하는 작업을 벌이면..

    메모리가 금방 차고 이에 대한 GC가 빈번하게 발생하는 그런 상황이 발생합니다..

    처리속도가 늦어지는 원인이 여기에서 나오는거에요..GC 때문에..

    그러나 new로 할당하는게 아니라면 문제는 없어요..

    오히려 new가 아니라면 스코프 차원에서 전자가 낫기도 합니다..

    변수의 영역이 for loop 안에만 존재하기 때문에..이로 인한 버그의 소지도 줄일수 있죠..

    그리고 객체 생성이란 관점에서 보시지 마세요..

    이거는 객체 생성이라기 보단 오히려 C나 C++에서 얘기하는 포인터 관점에서 봐야 합니다..

    즉 객체를 생성한다는건 내가 특정 메모리 공간을 이용해서 정보를 저장한다는 겁니다..

    이때 이를 두개의 변수가 동일한 객체를 가르키느냐..아니면 다른 것을 가르키느냐로 봐야 하는겁니다..

    이 문제를 객체 생성 관점에서 보시는 분들이 있는데..

    new를 사용해야 객체가 생성되는 겁니다..

    new를 사용하지 않으면 포인터 개념으로 가르키는 것일뿐입니다..



    1
  • return true
    2k
    2016-04-20 15:29:37

    제타건담님 자세한 답변 매우 감사드립니다.

    사실 자바 배울때 초반부에 배우는 기초적인 내용인데 새하얗게 망각해버리고 있었네요


    그리고 초반부에 설명해주신 부분에서 변수를 안에서 선언 후 객체를 매번 만들면 저런 문제가 발생하지만

    변수를 밖에 선언한 후 안에서 new로 객체를 매번 만들면

    그때는 매번 생성되었던 객체는 본인을 가르키는 변수가 없어지므로  비교적 가비지컬렉션에 의한 처리가 빨라서 문제가 덜 되는 건가요?

    어...이게 아닌거 같은데...으악 헤깔리네요... 매번 객체를 생성하면 안에 생성하든 밖에 생성하든 둘다 언급하신 부분이 문제가 되는거 아닌가요?

    0
  • asd
    16k
    2016-04-20 15:40:40

    제타건담 // 글을 보다가 좀 헷갈려서 여쭤보는데 혹시 전자랑 후자를 바꿔서 말씀하고계신건가요?

    [오히려 new가 아니라면 스코프 차원에서 후자가 낫기도 합니다..

    변수의 영역이 for loop 안에만 존재하기 때문에..이로 인한 버그의 소지도 줄일수 있죠..]

    이부분때문에 여쭤보는건데 혹시 잘못작성하신게 맞다면 윗부분부터 쭉 잘못쓰신건지도 답변부탁드립니다. GC쪽은 잘 몰라서 정확히 이해를 하고자함입니다.

    0
  • 제타건담
    6k
    2016-04-20 15:47:55

    원래 위에 저렇게 예시를 달아놓은건 기존것은 GC로 지정된다는 것을 알리기 위해

    이해하기 쉽게 하기 위해서지..

    GC로 지정된 메모리를 변수가 가르키는게 아닙니다..

    엄밀하게 얘기하면 GC로 지정된 메모리 구간은 변수가 가르키진 않는다고 보는게 맞습니다..

    역으로 GC 대상이 되는 메모리 공간을 특정 변수가 가르킬수 있다? 이게 말이 된다고 보시나요?

    GC 대상이 되는 메모리 공간은 특정 변수가 가르키는 것 없이 공중에 붕 뜬다고 보면 됩니다..

    지금 보면 for loop 안에 가르키고 있는 것은 new로 생성된 것이 아니라..

    for loop를 타기 전에 외부에서 이미 저장되어 있는 공간을 그대로 다른 변수로 가르키고 있게끔 하는것뿐이 없습니다..

    예시를 다르게 가보죠..

    어차피 주신 예시는 new가 없기 때문에 제가 new로 된 예시로 하나 알려드리죠..

    첫번째..

    String a = new String("aaa");
    String b = new String("bbb");
    String c = new String("ccc");
    
    for(int i = 0; i < 3000; i++){
        String d = a;
        String e = b;
        String f = c;
    }

    두번째

    String a;
    String b;
    String c;
    
    for(int i=0; i < 3000; i++){
        a = new String("aaa");
        b = new String("bbb");
        c = new String("ccc");
    }


    a,b,c는 모두 aaa,bbb,ccc는 가르킬겁니다..그러나 그 와중에 벌어지는 GC를 같이 고민해보세요..

    0
  • 큐럼
    982
    2016-04-20 15:49:04

    어느쪽이든 있던 값을 가비지로 보내고 새로운 객체를 가져오는거에는 변함이 없죠.

    그래서 성능상에서 어느게 좋다 나쁘다의 문제는 아닌 부분이구요.


    변수(객체)에 대한 정의가 C에비해 자유로운 자바의 경우에는 "쓰기 직전에" 정의하는 것을 추천한다고 합니다. 

    다만, 아래의 세 가지 경우를 봅시다. (본 질문과는 직접연관은 없지만 아마 도움이 될겁니다)

    //1
    String name = user.getUserName();
    for(int i = 0; i < 100; i++)
     System.out.println("name == " + name);
    //2
    for(int i = 0; i < 100; i++){
     String name = user.getUserName();
     System.out.println("name == " + name);
    }
    //3
    for(int i = 0; i < 100; i++){
     System.out.println("name == " + user.getUserName());
    }

    이 세가지의 경우를 보며는 user 객체의 name에 대한 게터를 반복문 안에서 주기적으로 호출하는데요,

    이럴 경우에는 같은 객체에 대한 메서드 호출이 줄어들 수 있는 1번이 더 낫다고 보면 될 거 같습니다.

    0
  • 제타건담
    6k
    2016-04-20 15:50:05

    // LichKing 

    아..맞네요..제가 착각했어요..ㅎㅎ..죄송합니다..댓글도 수정해놔야 겠네요..

    스코프 차원에서 보면 전자가 더 낫습니다..가 맞아요..

    죄송합니다..


    0
  • 제타건담
    6k
    2016-04-20 15:57:37

    // 큐럼

    그 정의는 어디까지나 문법적인 정의로서 얘기한것이지 자바의 퍼포먼스를 고민하며 한 말은 아닙니다..

    또한 가비지 컬렉션에 대해 너무 가벼이 보시는 경향이 있으신듯 한데..

    가비지 컬렉션을 우리가 직접 하질 않아서 이 부분에 대해 고민을 안할 뿐이지..

    사실 가비지 컬렉션이 발생하는 상황을 최소화 시키면서 개발해야 합니다..

    가비지 컬렉션이 발생한다는건 바꿔 말하면 유효 메모리가 없다는 것이고..

    유효 메모리가 없다는 것은 시스템적으로 어딘가 재활용할 수 있는데도 불구하고

    재활용 안하고 메모리 누수가 있다는겁니다..

    빈번한 가비지 컬렉션은 시스템 성능 저하의 원인이 되기도 합니다..

    이렇게 얘기하면 꼭 오해하시는 분들이 있어서 덧붙이면..

    적어도 JavaEE 시스템에서 가비지 컬렉션이 없게끔 개발할 수는 없습니다..그건 불가능하구요..

    그러나 가능한한 최소화 시켜서 개발할 수는 있는 겁니다..

    가비지 컬렉션을 최소화 시켜서 퍼포먼스를 올려야 하는거죠..

    0
  • asd
    16k
    2016-04-20 16:12:28

    제타건담 // 글쓰기에 앞서서 따지거나 시비거는건 아닙니다. 이해가 잘 안가거나 저와 생각이 다른부분에 대해서 여쭤보는거니 오해하지않으셨으면 좋겠구요(얼마전에 한번 좀 오해를 산적이있어서...ㅜㅜ).

    일단 제가 이해한바로는 제타건담님은 본문에서 2번으로 사용하는게 좋다고 말씀하신거같은데 사실 위에 써주신 말씀이 공감하기가 힘듭니다.

    [이런식으로 3000번을 다 돌면..실제로는 3개만 사용하고 나머지 8997개(3000 * 3 - 3)는 GC로 예약되어 있는거죠..]

    이런 표현을 하신부분이 있는데 보통 for문에서 객체를 생성하는경우엔 for문 내에서 해당 객체가 필요한 로직을 모두 수행한 후 for문바깥에서까지 그 객체를 쓰는일은 거의 없습니다.

    그러니 실제로 3개만 사용하고 나머지 8997개가 GC로 예약됐다는 표현보다는 마지막에 3개를 사용한 최후의 반복문이 돌아가는거고 이미 사용이 끝난 8997개가 GC로 예약된다. 라는 표현이 맞다고 생각합니다.

    그리고 new와 메서드로 객체를 호출할때가 다르다고하셨는데 이역시 보통 for문내에서 메서드를 호출해 객체를 가져와서 로직을 처리하는경우 호출할때마다 새로운 객체를 가져오는 로직이 있는경우가 대부분입니다.


    public Object getInstance(){
      return new Object();
    }
    
    while(true){
      Object obj = getInstance();
    }

    이런구조라는거지요. 싱글톤패턴으로 작성하거나 혹은 동일한 객체를 항상 가져오는거라면 for문 내에서 호출하는거 자체가 가독성을 떨어뜨리는일이죠.


    정리하면

    1. 어차피 for문 내에서 호출하는 객체는 팩토리 메서드로 호출하든 new 연산자로 직접 객체생성을 하든 새로운 객체를 가져오기 위해서 사용하는경우가 대부분인데 그걸 메서드와 new를 구분하는게 의미가 있는가?


    2. for()문 내에서 객체를 생성 혹은 호출하는 경우는 for문 내에서 로직에만 사용되는 경우일텐데 이것도 GC를 생각해서 바깥에다가 변수선언을 해야하는가? 오히려 GC까지 고려해야하는 부분이라면 반복문이 종료된 후 최대한 빨리 수거될수있게 for문 내에 변수를 선언해야하는게 아닌가?


    에 대해서 좀 궁금하네요.


    1
  • 제타건담
    6k
    2016-04-20 16:48:21

    // LichKing

    그렇게 얘길 한 이유는 철저히 메모리 사용관점에서 얘기를 한겁니다..

    무슨 뜻이냐면..for loop 밖을 벗어난다 해도 GC로 예약되어 있는 메모리 공간이 해제가 된다는건 아닌거죠..

    이 부분도 제가 잘못 얘기한 부분이 있네요..

    지금 생각해보니..for loop 문을 벗어나면 9000개 모두가 GC 대상이 되겠네요..

    자바 입장에서 보면 for loop 안에 있는 3개도 밖에서는 사용되는 것이 아니기 때문에 얘도 GC 대상으로 삼겠네요..

    아주아주 엄밀하게 따지면..

    for loop문을 맨 마지막번에 돌때 마지막 변수에 할당하는 시점이 8997이 되겠네요..

    그리고 for loop 문을 모두 마치면 스코프 관계 때문에 그 3개 조차도 GC 대상이 되어서..

    9000개 모두 GC 대상이 되겠네요..

    말씀하신 내용중

     마지막에 3개를 사용한 최후의 반복문이 돌아가는거고 이미 사용이 끝난 8997개가 GC로 예약된다. 라는 표현이 맞다고 생각합니다.

    이게 마지막 loop문 돌아가는 시점을 얘기하신거라면 그게 맞아요..ㅎㅎ..

    세심하게까지 챙기진 못했네요..ㅎㅎ..

    그리고 제가 얘기한 것중에 메소드를 얘기한 부분은 없어요..외부에서 가져온 거라고만 얘기했죠..외부에서 가져온거라고 얘기한 것도 질문의 예시에서 참조되는 곳이 for 문 밖에서 만들어진 배열이었기에 그렇게 얘기한거구요..

    메소드는 다른분이 한거 같은데..


    그리고 변수 선언 위치는 중요한게 아닙니다..변수 선언 위치는 스코프만 연관이 있는거죠..

    저는 for loop 안에서의 new에 대해서만 얘기했지..변수 선언 위치를 어디다 두어야 한다..라고 언급한건 없어요..

    제 댓글 중 무엇을 보시고 변수 선언 위치와 메소드 얘기를 하시는건지 모르겠네요..


    0
  • 여미융
    62
    2016-04-20 17:05:03

    댓글보고 정말 많은 도움이 된것같습니다. -_-ㅋㅋ


    저도 혼자 테스트해보면서 이런저런 생각을 가질수 있는 기회가 되었네요 ㅎ

    0
  • asd
    16k
    2016-04-20 17:25:08

    제타건담 // 글 자체가 변수선언위치에 대한 얘기라 제가 계속 그거랑 연관지어서 생각했던것같습니다. 답변감사합니다.ㅎㅎ

    0
  • 로그인을 하시면 답변을 등록할 수 있습니다.