애플리케이션 응답성 향상을 위해 동시성 사용하기

아이폰 2011. 3. 15. 23:00



1. 애플리케이션에서의 응답성

아이폰, 아이패드를 포함한 모든 애플리케이션의 사용자 인터페이스에 있어서 응답성은  중요한 요소중에 하나이다.

많은 시간을 소비하는 태스크를 사용자 인터페이스가 동작하는 쓰레드에서 수행하면, 해당 태스크가 종료하기 까지

사용자는 어떠한 인터렉션도 행할 수가 없기 때문이다.  트위터의 글목록을 가져와서 보여주는 앱을 생각해보자.

최신글 목록과 사용자 프로필 이미지를 가져오는 동안, 버벅이 되며 먹통이 되면 누가 이 앱을 사용하겠는가?

이 포스팅에서는 Cocoa  Touch에서 지원하는 동시성 향상을  방법에 대해서 알아보겠다. 

MacOsX와 iOS에서 지원하는  사용자 인터렉션 응답성을 향상시키기 위한 다음과 같은 동시성 방법들이 있다.    
NSThread / NSObject가 지원하는 백그라운드/메인 쓰레드,  C기반의 POSIX 쓰레드,  비동기 기반의 메소드,  NSOperation /NSOperationQueue 등이 있다.  각각의  추상화 수준(사용 용이성)과 복잡성이 다르다.  추상화 수준이 높은 방법일 수록 
하드웨어 의존적인 부분을 감추어주고 쉽게 사용할 수 있어서, 비지니스 로직에 더 집중 할 수 있다.  좀더 세밀한 컨트롤과
뛰어난 성능을 원한다면 C 기반의 동시성 API 를 사용하자.   



2. NSOperation /  NSOperationQueue

웹에서 사진들의 목록을 받아와 이미지와 함께 사진의 이름을 출력하는 플리커 애플리케이션을 생각해보자.

사진들의 목록을 받아오고, 목록에 해당하는 이미지 각각을 로딩하는데 상당한 시간이 소모될 것이다. 이러한 작업을

아이폰 애플리케이션의 메인쓰레드에서 수행할 경우, 애플리케이션은 상당히 버벅거리게 된다. 백그라운드 쓰레드로 

이미지를 가져오는 방법을 선택하더라도 성능의 향상은 있겠지만 그 효과는 미미하다. 

하나의 이미지를 가져오는 작업에 하나의 쓰레드를 할당하여 완료되면 이미지를 목록을 추가하는 방식이 자연스러운 

애플리케이션이 될 것이지만,  몇 개의 쓰레드를 할당하는 것이 성능의 저하를 가져오지 않고 최적의 성능을 낼 것인지는

하드웨어에 의존적이어서 정확히 알 수가 없다.  아이폰에서 제공하는 비동기 방식의 메소드를 사용하여 해결 할 수 있지만, 

좀더 추상화 수준이 높으면서 하드웨어 변경에도 코드의 변경이 거의 없는 NSOperation/NSOperationQueue 를 사용해보자.

NSOperatinoQueue 는 내부적으로 최적의 성능을 낼 수는 쓰레드의 개수를  생성하고 관리해 준다. 

NSOperation은 NSObject를 상속하고, 하나의 작업단위를 캡슐화하는 추상클래스이다. 




NSOperation을 상속하여 커스텀 클래스를 작성하거나, NSBlockOperation , NSInvocationOperation을 
사용하여 작업을 캡슐화하고 NSOperationQueue에 삽입하면 별도의 쓰레드에서 태스크를 수행할 수 있다. 
이때 그래픽 사용자 인터페이스를 변경하는 작업은 반드시 메인쓰레드상에서 수행해야함을 주의하자!!
NSOperation을 상속하는 클래스는 main 이라는 이름의 메소드에서 독립적인 작업을 캡슐화한다.
NSInvocationOperation은 별도의 쓰레드에서 호출할 객체와 셀렉터를 지정할 수 있다.  



3. NSBlockOperation 

NSBlockOperation은 Lisp, Ruby와 같은 언어의 클로져와 유사한 Block 이라는 요소를 사용한다.  Block은 마치
데이터 스콥을 공유하는 함수와 유사하며, Block으로 지정한 코드를 캡슐화하여 별도 쓰레드로 동작시킨다.
NSOperation / NSInvocationOperation 과 NSOperationQueue를 사용하는 방법도 훌륭하다.  하지만 NSBlockOperation을
사용하면 동일한 성능의 장접을 갖되,  연관된 코드를 한곳에 집중 시킬수 있는  부수적인 장점이 생긴다.
그러나 아이폰 운영체제의 하위 호환성이 떨어지는 것이 단점이다.  



 블럭은 사용하는 코드는 아래와 같다. 

NSBlockOperation의 blockOperationWithBlock 메시지의 인자로 블럭을 정의하여 인자로 전달하였다. 

블럭 ^{} 의 내부에서  긴 시간이 소모되는 코드를 수행하고, 작업이 완료되면 MainThread 에서 GUI를 변경하도록 하고 있다.

블럭 내 코드는 별도의 쓰레드에서 실행되기 때문에 자체적인 릴리지 풀을 생성하여 사용해야한다. 마지막으로 블럭에 정의한

작업을 수행하기 위해 NSOperationQueue(workQueue)에 추가하고 있다. 

 

NSBlockOperation *fetchImageOp = [NSBlockOperationblockOperationWithBlock:^{

NSAutoreleasePool *localPool;

@try {
// Autorelease pool 생성
localPool = [[NSAutoreleasePool alloc] init];
// 시간이 소요되는 작업 수행 
[selfperformSelectorOnMainThread:@selector(storeImageForURL:) // 메인쓰레드에서 GUI 변경

withObject:result 

    waitUntilDone:NO];

}

@catch (NSException * exception) {

NSLog(@"Exception: %@", [exception reason]);

}

@finally {
// Autorelease pool 해제
[localPool release];

}

}];
// NSOperationQueue에 추가 
[workQueue addOperation:fetchImageOp];  

 


4. 블럭 선언 및 정의하기
다음과 같이 익명으로 정의되어, 메시지의 파라미터로 전달될 수 있다. 

int (^cubeIt)(int);// 블럭변수 선언    
cubeIt = ^(int num) { return num * num * num; };// 블럭정의


다음과 같이 익명으로 정의되어, 메시지의 파라미터로 전달될 수 있다.

(returnDataType (^) ( parameterType1, parameterType2, ...))blockName


 



'아이폰' 카테고리의 다른 글

애플 푸시 서비스  (0) 2011.04.07
Sprite Sheet 제작툴 Zwoptex  (0) 2011.03.16
애플리케이션 응답성 향상을 위해 동시성 사용하기  (0) 2011.03.15
OpenGL ES 게임 프레임웍  (0) 2011.02.14
OpenGL ES 사용  (0) 2011.02.14
OpenGL ES 사용설정  (0) 2011.02.13

[Lucene] java.lang.OutOfMemoryError : Java heap space

검색엔진로그 2011. 3. 15. 12:43



루씬을 사용하는 도중  Searcher 클래스에서 다음과 같은 에러가 발생했다. 

java.lang.OutOfMemoryError: Java heap space
        at org.apache.lucene.index.SegmentReader$Norm.bytes(SegmentReader.java:463)
        at org.apache.lucene.index.SegmentReader.getNorms(SegmentReader.java:1017)
        at org.apache.lucene.index.SegmentReader.norms(SegmentReader.java:1024)
        at org.apache.lucene.search.TermQuery$TermWeight.scorer(TermQuery.java:79)
        at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:210)
        at org.apache.lucene.search.Searcher.search(Searcher.java:67)

 색인파일이 31G 정도 쌓였을 쯤 발생한 에러인데,  색인이 너무 커서, 검색도중  메모리가 부족해지는 현상이었다.  
임시방편으로 
set  JAVA_OPTS="-Xms512m -Xmx1024m"

명령으로  JVM의 힙메모리 크기를 늘려주었더니 뻣는 현상은 해결이 되었다.  하지만 색인 파일은 
자료가  쌓일수록 더 커질 것이기 때문에 언젠가 다시 메모리 부족현상이 발생하게 될 것이다.  
좀더 근본적인 해결책을 찾아봐야 겠다. 

현재는 다수의 램디렉토리와 단일 파일디렉토리를 사용하여 색인을 수행하고 있다.  즉 하나의 파일 색인기를 사용하고 있는데 
이 파일 색인기를 하나만 사용할 것이 아니라  다중으로 분리하여 구성하면 위 문제에 대한 해결책이 어느정도 될 것 같다. 
이렇게 하면 합너에 읽어들이는 색인파일의 크기도 줄어들 것이고, 최적화도 색인기별로 별도로 수행할 수 있으며, 
하나의 색인기가 에러가 발생하더라도 나머지 색인기를 사용할 수 있기 때문에 큰 장점이 된다. 
다중 색인기에서 동시에 검색을 지원하는 루씬 클래스도 지원되기 때문에 검색모듈을 구축하는 부분도 크게 달라지지
않는다.  

[Java] 어노테이션(Annotation)

언어로그/Java 2011. 3. 15. 01:40



프로그래밍을 하면서 @Override 와 @Test와 같은 어노테이션들을 많이 사용했지만,  그 중요성이나 의미를 
깊이 있게 생각해보지는 못했었다.  단순히 컴파일러에게 알려주기 위한 표식정도로 생각했었다.  그런데  Spring Roo 와 
Spring3.0 과 같은 최신 프레임웍들의 변화 경향을 보면,  어노테이션을 적극활용하는 쪽으로 변화되고 있다.  어노테이션을
사용하여 코드의 작성량도 한결 줄어들었다고 한다.  어노테이션들의 어떤 특성을 활용한 것일까?  어노테이션이란 뭘까?
최신 프레임웍들에 변화경향을 보기에 앞서, 어노테이션에 대해서 먼저 알아보았다.



1. Annotation 이란?
Annotation은 JEE5 부터 새롭게 추가된 문법요소이다.  사전적으로는 "주석"이라는  의미를 가지고 있으며, 의미대로
자바 코드에 주석처럼 달아 특수한 의미를 부여해준다.   이 특별한 의미는 컴파일 타임 또는 런타임에 해석될 수 있다.
 

 

2.  기존 웹애플리케이션들의 문제점
기존의 자바 웹애플리케이션들은 선언적인 프로그래밍 방식을 사용한다.  선언적이란  프로그램의 전체 및 각 레이어별
구성과 설정값들을 외부의 XML 설정파일에 명시하는 방식을 의미한다.  변경이 될 수 있는 데이터들을 최대한 코드가 아닌
외부설정파일에 분리하기 때문에 변경 요구사항이 들어왔을 때, 재컴파일 없이도 쉽게 변경사항을 적용할 수 가 있다. 
유연성이란 장점을 얻었지만,  단점 또한 존재한다. 프로그램 작성을 위해 매번 많은 설정파일을 작성해야 한다는 것이다. 
그 규모가 커질수록 설정의 양도 많아지게 되며 이를 잘 구조화 할 수 있는 방법도 필요하게 된다. 또 하나의 단점은 이것보다
좀 더 크다.   도메인 데이터 처리정보가 Model 클래스, 서비스 클래스,  XML 설정파일에 분산되어 있어서,   이를 확인하기
위해서는 Model , Service 클래스와 XML 설정파일을 모두 뒤져야 한다는 것이다. 




3. Annotation의 사용했을 때의 장점은? 
어노테이션을 사용하면 위와 같은 문제를 해결 할 수 있다.  데이터에 대한 유효성 검사조건을 어노테이션을 사용하여
Model 클래스에 직접 명시함으로써 해당 데이터들에 대한 유효조건을 쉽게 파악할수 있게되며, 코드의 양도 줄어든다.
(엄밀히 말하면,  코드의 양은 줄어들지 않는다. 하지만 코드가 깔끔해지고, 어노테이션의 재사용도 가능해진다. )



4. 그렇다면 XML 설정은 사용하지 않아야 하는가?
앞서 말했다시피, XML은 유연성을 확보해준다.   어노테이션을 사용하여 한번 빌드된 코드는 수정을 위해서는 
재컴파일 해야하는 단점이 있다.  애플리케이션 전체적인 설정이나 디플로이 환경에 따라 변경되는 사항들은 XML 설정을
사용하자.  각각의 장단점을 파악하고, 언제 무엇을 사용해야할지 아는 것이 중요하다. 


그럼 이제부터 어노테이션을 사용해보자. 사용을 위해 먼저 선행지식들에 대해 잠깐 알아보자. 


5. 일반적인  어노테이션의 용도 
어노테이션은 크게 ① 문서화 ② 컴파일러 체크 ③ 코드 분석을 위한 용도로 사용된다.  문법적으로는 @기호가 
붙은 심볼을 사용하며 패키지, 클래스, 메소드,  프로퍼티, 변수에 명시할 수 있다. 이 어노테이션이 붙은 소스를 
컴파일 시에 수집하여 API 문서화 할수도 있지만 기존에 JavaDoc 라는 좋은 문서화 도구가 있기 때문에 "문서화" 는 
가장 비중이 낮은 어노테이션의 사용법이다. 
또한 컴파일 타임에 에러를 발생시켜 주어 개발자에서 위험요소를 경고해주거나 확인하는 목적으로도 사용된다. 
가장 큰 비중을 갖는 것은 코드 분석 또는 "메타-데이터" 로서의 용도이다. 메타-데이터 란 데이터를 위한 데이터, 즉 
데이터에 대해 설명하는 데이터를 의미한다.  메타데이터로서 어노테이션의 효용을 가장 잘 느낄 수 있는 부분이
JEE 설정과 유효성 검사 부분이다. 



5.  어노테이션의 분류
어노테이션은 메타데이터 저장을 위해 클래스처럼 멤버를 갖을 수 있다. 이 멤버의 개수에 따라 Marker 어노테이션,
Single-value,  Full 어노테이션으로 분류할 수 있다. 


①  Marker 어노테이션
     - 멤버 변수가 없으며, 단순히 표식으로서 사용되는 어노테이션이다. 컴파일러에게 어떤 의미를 전달한다. 

②  Single-value  어노테이션
    - 멤버로 단일변수만을 갖는 어노테이션이다. 단일변수 밖에 없기 때문에 (값)만을 명시하여 데이터를 전달할 수 있다.  

 Full 어노테이션
    -멤버로 둘 이상의 변수를 갖는 어노테이션으로, 데이터를 (값=쌍)의 형태로 전달한다. 




6.  빌트인(Built-in) 어노테이션 
자바 SDK에서 지원하는 어노테이션으로 @Override, @Deprecated, @SupressWarning 등이 있다. 



@Override 
 어노테이션은 현재 메소드가 수퍼클래스의 메소드를 오버라이드한 메소드임을 컴파일러에게 명시한다. 만일
수퍼 클래스에 해당하는 메소드가 없으면 컴파일러가 인지하고 에러를 발생시켜 준다. 

 

@Deprecated 

 마커 어노테이션으로  차후 버전에 지원되지 않을 수 있기 때문에 더 이상 사용되지  말아야할 메소드를 나타낸다.  

 특이하게 더이상 사용되지 말아야할 메소드와 같은 라인상에 놓여져야 한다. (이유모름)

(두 플래그  -deprecated 또는 Xlint:deprecated 중 하나와 javac 명령어를 사용하여 컴파일러 경고를  켜야만, 

 컴파일러 에러를 발생시켜준다)

@SupressWarning
의미데로 경고를 제거하는 어노테이션이다. Object형을 엘리먼트로 하는 컬렉션을 사용하면,  컴파일러 경고가 발생하는데
이 어노테이션을 사용하여 프로그래머의 의도적인 Object 형 사용임을 알려 경고를 제거할 수 있다. 




7. 커스텀(Custom) 어노테이션 

클래스와 같이 어노테이션을 임의로 정의하여 사용할 수 있다. 어노테이션은 interface 키워드 앞에 @를 붙여 표시한다. 


1) 커스텀 어노테이션 정의하 



// 메소드와 클래스가 여전히 작업중임을 나타내기 위해 정의한 마커 어노테이션
public @interface InProgress { }

어노테이션 정의파일을 컴파일하고, 이 파일을 클래스패스에서 참조할 수 있으면 다른 소스코드상에서 어노테이션을

사용할 수 있다. 



2) 멤버 추가하기 

어노테이션 유형은 멤버변수를 가질 수 있으며, 이 변수들  컴파일시 또는 런타임에 메타-데이터로서 사용될 수 있다.  
이렇게 정의를 하면 자동으로 
accessor와 mutator를 제공해준다.


// 해당 태스크가 완료되야함 나타내는 어노테이션

public @interface TODO {

  String value();           

}


정의한 어노테이션을 다음과 같이 사용한다. 


@TODO("Figure out the amount of interest per month")

public void calculateInterest(float amount, float rate) {

  // ...

}


단일 멤버를 갖을 경우에만 위와 같이 사용할 수 있다. 



3) 디폴트 값 설정하기 
 

public @interface GroupTODO {


  public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION };


  Severity severity() default Severity.IMPORTANT;

  String item();

  String assignedTo();

  String dateAssigned();

}


디폴트값을 사용한 예는 다음과 같다. 

@GroupTODO(

    item="Figure out the amount of interest per month",

    assignedTo="Brett McLaughlin",

    dateAssigned="08/04/2004"

  )

  public  void calculateInterest(float amount, float rate) {

    // ...

  }





4
) 메타-어노테이션

어노테이션에 사용되는 어노테이션으로 해당 어노테이션의 동작대상 및 보여타임을 결정한다. 


① @Target 메타-어노테이션

   어노테이션이 적용되는 동작 프로그래밍 

엘리멘트를 지정하며, 다음과 같은 값들이 있다. 

package java.lang.annotation;


public enum ElementType {

  TYPE,       // Class, Interface, enum 

  FIELD,                       // 프로퍼티 (enum 나열값들은 제외)

  METHOD,       // 메서드

  PARAMETER,       // 메서드 파라미터

  CONSTRUCTOR,                     // 생성자

  LOCAL_VARIABLE,       // 로컬변수 또는 Catch문

  ANNOTATION_TYPE,       //  어노테이션(메타 어노테이션)_

  PACKAGE       //  자바 패키지

}




② @Retention 메타-어노테이션 

     자바컴파일러가 어노테이션을 다루는 방법과 관련이 있다.  소스파일, 클래스파일, 런타임 중 어느시점까지

    어노테이션을 보유하고 있을 것인지를 결정한다. 


package java.lang.annotation;


public enum RetentionPolicy {

  SOURCE, // 어노테이션이 컴파일러에 의해 버려짐

  CLASS, // 어노테이션이 클래스파일에 저장되지만, JVM에게 무시됨

  RUNTIME // 어노테이션이 클래스파일에 저장되며, JVM에 의해 읽혀짐 

}

 

위 세값 중 하나를 인자로 취해 다음과 같이 사용된다. 


@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings {

  ...

}






'언어로그 > Java' 카테고리의 다른 글

(2) 리플렉션(Reflection) 사용하기  (0) 2011.04.07
(1) 리플렉션(Reflection)  (3) 2011.04.07
예외처리 (Exception Handling)  (0) 2011.03.24
[Java] 어노테이션 사용하기  (1) 2011.03.19
[Java] 어노테이션(Annotation)  (2) 2011.03.15
[자바] (1) 자바소개  (0) 2011.03.10
  • alecsander 2012.05.30 13:05 ADDR 수정/삭제 답글

    좋은 자료 감사합니다^^ 담아가요~

  • 반가 2016.06.09 12:59 ADDR 수정/삭제 답글

    좋은 정보 잘보고 갑니다