[후기] 테스트 주도 개발

  • 지은이: 켄트 백
  • 옮긴이: 김창준, 강규영
  • 출판사: 인사이트
  • 출간일: 2014년 2월

 포인트1. 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.

 포인트2. 중복을 제거한다.

프로그래밍 순서

  • 빨강 – 실패하는 작은 테스트를 작성한다.
  • 초록 – 빨리 테스트가 통과하게끔 만든다.
  • 리팩토링 – 일단 테스트를 통과하게만 하는 와중에 생겨난 모든 중복을 제거한다.

매크로를 이용하여 테스트를 작성한 후 얻은 교훈

  • 테스트가 충분히 빨라서 내가 직접, 자주 실행할 수 있게끔 만들자.
  • 많은 오류가 반드시 어머어마한 양의 문제는 아니다.

TDD가 XP의 어떤 부분을 향상 시키는가?

  1. 짝 프로그래밍
    TDD를 하면서 작성하게 되는 테스트는 짝 프로그래밍 과정에서 뛰어난 의사소통 수단이 된다.
  2. 활기차게 일하기
    XP에서는 기운이 있을 때 일을 시작해서 지치면 그만할 것을 권유한다. 테스트를 통과하지 못하겠거나, 어떤 테스트 두 개를 동시에 통과하도록 만들 방법을 모르겠다면 쉬어라.
  3. 지속적인 통합
    테스트를 좀더 자주 통할할 수 있게 해주기 때문에 아주 훌륭한 자원이 된다. 새로운 테스트를 통과시킨 후 중복을 제거했다면 커밋한다. 그렇게 되면 체크인 간격이 15~30분 정도로 단축된다.
  4. 단순 설계
    테스트를 통과하기 위해 필요한 만큼만 코딩하고 모든 중복을 제거한다면 요구사항에 딱 들어맞는 설계를 얻게 될 것이다.
  5. 리팩토링
    중복 제거 규칙은 리팩토링의 또 다른 이름이다. 테스트가 있다면 더 큰 리팩토링을 수행하더라도 시스템의 행위가 변하지 않았다는 자신감을 얻을 수 있게 된다.
  6. 지속적인 전달
    TDD 테스트들이 정말 당신 시스템의 MTBF(Mean Time Between Failure)를 개선한다면, 고개을 혼란시키지 않으면서도 훨씬 더 자주 코드를 출시할 수 있을 것이다.

추천 도서

  • Smalltalk Best Practice Patterns

[Java] Lambda(Stream) 는 항상 좋을까?

[원글] http://www.javacodegeeks.com/2015/12/3-reasons-shouldnt-replace-loops-stream-foreach.html

Java 1.8이 출시된 후 많은 블로그 및 홈페이지에서 Lambda 를 소개하는 글들이 많이 올라와 있습니다. 그렇지만 대부분의 글에 Lambda의 장점들에만 치중하는 내용이 대부분인데 오늘 RSS 피드에 괜찮은 내용이 있어 요약해 봅니다.

1. 성능

Lambda(Stream) 와 For-loop 방식의 처리에는 성능의 차이가 발생된다.

ArrayList, for-loop : 6.55 ms
ArrayList, seq. stream: 8.33 ms
int-array, for-loop : 0.36 ms
int-array, seq. stream: 5.35 ms

위의 성능의 차이는 어찌 보면 큰 차이는 아닌것 같지만 대량의 데이터를 처리해야 하는 로직에 포함된 Stream API라면 많은 성능 차이를 일으킬 수 있습니다.

2. 가독성

저도 간단하게 Stream API를 이용하여 Filter및 Collector를 이용하면서 소스 코드가 확연히 줄어든 경험이 있지만 가독성은 Stream API를 사용하기 전보다 떨어지는 것을 느꼈습니다.

단순 리스트에서 동일한 값을 뽑아내는 형태의 것은 문제가 없다고 생각하지만 Filter를 이용해서 값을 추출하고 추출한 값을 다시 가공하고 연산하여 데이터를 취합하는 형태를 구현 한다면 훨씬 복잡하고 이해하기 힘든 코드가 될 것이라는 생각입니다.

또한 다수의 사용자가 동일한 코드를 작업하는 환경이라면 모든 사용자가 익숙하지 않다면 Stream API 사용을 자제하는 것이 좋다고 생각됩니다.

3. 유지보수

대부분의 코드는 단발성이 아닌 수정이 빈번하게 발생되는 경우가 있습니다. Stream API를 사용하는 경우 StackTrace에 불필요한 내용이 출력되어 오류 분석에 어려움이 있을 수 있습니다.

[For-loop]

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:13)

[Lambda]

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.lambda$1(Test.java:18)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
    at Test.lambda$0(Test.java:17)
    at java.util.Arrays$ArrayList.forEach(Arrays.java:3880)
    at Test.main(Test.java:16)

이상입니다.

[Java] Dropbox API(Upload, Download, List)

Dropbox API 사용법

iOS Ad-hoc 버전 릴리즈 및 팀의 파일 공유를 위해 Dropbox를 사용하는데 이번에 CI(continuous integration)를 하려고 마음을 먹어서 단계별 기능을 확인하고 있다.

Dropbox API 사용법

https://www.dropbox.com/developers-v1/core/start/java 링크에 상세하게 설명되어 있다.

위의 단계에서 주의해야 할 사항은 AccessToken을 얻는 부분이다. 예제대로 따라 하면 AccessToken을 매번 새로 생성하기 때문에 재사용이 안된다.

그렇기 때문에 App Home – Settings – Generated access token 을 이용하여 AccessToken을 생성한다.

[DropboxAPI.java]

import com.dropbox.core.DbxClient;
import com.dropbox.core.DbxEntry;
import com.dropbox.core.DbxException;
import com.dropbox.core.DbxWriteMode;

import java.io.*;

public class DropboxAPI {   


    public DbxEntry.File uploadFile(DbxClient client, String fileName, long fileLength, byte[] fileBytes) throws IOException, DbxException {
        ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
        DbxEntry.File uploadedFile = client.uploadFile("/CoozplzFile/" + fileName, DbxWriteMode.add(), fileLength, bais);
        return uploadedFile;
    }


    public DbxEntry.WithChildren listAllFiles(DbxClient client) throws DbxException {
        DbxEntry.WithChildren children = client.getMetadataWithChildren("/CoozplzFile");
        return children;
    }


    public DbxEntry.File downloadFile(DbxClient client, String sourceFile, File downloadedPath) throws IOException, DbxException {
        FileOutputStream fos = new FileOutputStream(downloadedPath);
        DbxEntry.File downloadedFile = client.getFile("/CoozplzFile/"+ sourceFile, null, fos);
        fos.close();
        return downloadedFile;
    }
}

[DropboxAPITest.java]

import com.dropbox.core.*;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.*;
import java.util.List;
import java.util.Locale;

public class DropboxAPITest {

    public static String ACCESS_TOKEN = "ACCESS_TOKEN";

    DbxClient client;


    @Before
    public void setup() {
        DbxRequestConfig config = new DbxRequestConfig("Sample/1.0", Locale.getDefault().toString());
        client = new DbxClient(config, ACCESS_TOKEN);
    }


    @Test
    public void testListFiles() throws DbxException {

        DropboxAPI dropboxAPI = new DropboxAPI();
        DbxEntry.WithChildren children = dropboxAPI.listAllFiles(client);
        Assert.assertNotNull(children);
        List<DbxEntry> list = children.children;
        System.out.println(list);
        Assert.assertNotNull(list);
    }


    @Test
    public void testUpload() throws IOException, DbxException {
        DropboxAPI dropboxAPI = new DropboxAPI();


        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        InputStream in = DropboxAPITest.class.getResourceAsStream("101-N101_c.ai");

        byte[] buf = new byte[1024];
        while (true) {
            int readSize = in.read(buf);
            if (readSize < 0) {
                break;
            }
            baos.write(buf, 0, readSize);
        }
        DbxEntry.File uploadedFile = dropboxAPI.uploadFile(client, "101-N101_c.ai", baos.size(), baos.toByteArray());
        Assert.assertEquals(baos.size(), uploadedFile.numBytes);
        System.out.println("Numbers of bytes = " + uploadedFile.numBytes);
        baos.close();
    }


    @Test
    public void testDownload() throws IOException, DbxException {
        DropboxAPI dropboxAPI = new DropboxAPI();

        DbxEntry.File file = dropboxAPI.downloadFile(client, "101-N101_c.ai", new File("D:/101-N101_c.ai"));

        String url = client.createShareableUrl(file.path);
        System.out.println(url);

        Assert.assertTrue(url.contains("https://www"));

    }


}

오픈소스 파일별 라인수

집에서 동영상으로 코딩 가이드에 대한 강좌를 보는 과정중에 재미난 내용이 있어 python을 사용해서 분석해 보았습니다.

해당 동영상에 나온 내용은 빠른 시일내에 블로그에 정리해서 올릴려고 합니다. 좋은 내용들이 많아서…

프로그래밍 가이드에는 왜 한 라인에 80자만 사용하라고 하는 것인가?

동영상 강좌에 나온 내용으로는 예전 환경은 콘솔 + 작은 모니터 + IDE없는 이어서 한 줄에 80라인을 넘기면 보기가 어려워서 그렇다고 설명을 하였습니다.

그러면서 나온게 최근에 사용되는 오픈소스 프로젝트인 JUnit, Ant와 몇몇가지를 예를 들어 최근에는 80자가 넘는 소스들도 많이 있다고 하였습니다.

그렇지만 한 줄에 너무 많은 글자를 쓰면 가독성이 떨어진다고 단지 좌우스크롤이 안될 정도로만 하라고 설명하셨습니다.

분석

그렇다면 제가 자주 사용하는 라이브러리들은 한 줄에 어느정도의 평균 글자수 와 한 파일에 몇라인이 포함되어 있는지 확인을 해보았습니다.

분석은 소스 폴더에 포함된 java로 확장자를 가지는 파일을 대상으로 하였습니다.

[분석도구]

  1. python 2.7
  2. matplotlib
  3. OSX(Yosemite)

[분석대상]

  1. commons-lang
  2. commons-net
  3. Ant
  4. Open JDK7

결과

결과는 어느정도 예상한대로 commons-lang 과 commons-net의 경우는 평균 100자를 넘지 않는 수치가 나왔습니다. 그렇지만 의외로 open-jdk7에서 평균 400자를 넘는 파일이 있어 확인을 해보니 그럴만한 이유가 있었습니다. 기타 참조

commons-lang

commons-lang_result

commons-net

commons-net_result

open-jdk7

jdk_result

분석 소스

import os
import fnmatch
import matplotlib.pyplot as plt

class FileVO:
def __init__(self, filename, file_size, line_of_file, avg_char):
self.file_size = file_size
self.line_of_file = line_of_file
self.avg_char = avg_char
self.filename = filename

def __str__(self):
return '%-20s / size=%5d, line=%5d, chars(AVG)=%5d' % (self.filename, self.file_size, self.line_of_file, self.avg_char)

if __name__=='__main__':
matches = []
source_path = '/Users/coozplz/Downloads/sources/src-jdk'
for root, dirnames, filenames in os.walk(source_path):
for filename in fnmatch.filter(filenames, '*.java'):
found_file = os.path.join(root, filename)
file_size = os.path.getsize(found_file)
js_file = open(found_file)
lines = js_file.readlines()
sum_of_chars = 0
for line in lines:
sum_of_chars += len(line)
vo = FileVO(filename, file_size, len(lines), sum_of_chars / len(lines))
# print vo
matches.append(vo)
js_file.close()

plt.title("%s(%d files)" % (source_path, len(matches)))
for match_file in matches:
plt.scatter(match_file.line_of_file, match_file.avg_char)
plt.ylabel("Average chars of line")
plt.xlabel("Lines per file")
plt.grid()
plt.show()

기타

평균글자수가 가장 많은 파일 : launcher.java
아래와 같은 코딩이 되어 있어 평균 글자수 길이가 비약적으로 많이 나오는거 같습니다. 나머지 파일들도 마찬가지라고 생각합니다.

소스코드

package sun.launcher.resources;

import java.util.ListResourceBundle;

public final class launcher extends ListResourceBundle {
protected final Object[][] getContents() {
return new Object[][] {
{ "java.launcher.X.macosx.usage", "nThe following options are Mac OS X specific:n -XstartOnFirstThreadn run the main() method on the first (AppKit) threadn -Xdock:name="n override default application name displayed in dockn -Xdock:icon=n override default icon displayed in docknn" },
{ "java.launcher.X.usage", " -Xmixed mixed mode execution (default)n -Xint interpreted mode execution onlyn -Xbootclasspath:n set search path for bootstrap classes and resourcesn -Xbootclasspath/a:n append to end of bootstrap class pathn -Xbootclasspath/p:n prepend in front of bootstrap class pathn -Xdiag show additional diagnostic messagesn -Xnoclassgc disable class garbage collectionn -Xincgc enable incremental garbage collectionn -Xloggc: log GC status to a file with time stampsn -Xbatch disable background compilationn -Xms set initial Java heap sizen -Xmx set maximum Java heap sizen -Xss set java thread stack sizen -Xprof output cpu profiling datan -Xfuture enable strictest checks, anticipating future defaultn -Xrs reduce use of OS signals by Java/VM (see documentation)n -Xcheck:jni perform additional checks for JNI functionsn -Xshare:off do not attempt to use shared class datan -Xshare:auto use shared class data if possible (default)n -Xshare:on require using shared class data, otherwise fail.n -XshowSettings show all settings and continuen -XshowSettings:alln show all settings and continuen -XshowSettings:vm show all vm related settings and continuen -XshowSettings:propertiesn show all property settings and continuen -XshowSettings:localen show all locale related settings and continuennThe -X options are non-standard and subject to change without notice.n" },
{ "java.launcher.cls.error1", "Error: Could not find or load main class {0}" },
{ "java.launcher.cls.error2", "Error: Main method is not {0} in class {1}, please define the main method as:n public static void main(String[] args)" },
{ "java.launcher.cls.error3", "Error: Main method must return a value of type void in class {0}, please ndefine the main method as:n public static void main(String[] args)" },
{ "java.launcher.cls.error4", "Error: Main method not found in class {0}, please define the main method as:n public static void main(String[] args)" },
{ "java.launcher.ergo.message1", " The default VM is {0}" },
{ "java.launcher.ergo.message2", " because you are running on a server-class machine.n" },
{ "java.launcher.init.error", "initialization error" },
{ "java.launcher.jar.error1", "Error: An unexpected error occurred while trying to open file {0}" },
{ "java.launcher.jar.error2", "manifest not found in {0}" },
{ "java.launcher.jar.error3", "no main manifest attribute, in {0}" },
{ "java.launcher.opt.datamodel", " -d{0}t use a {0}-bit data model if availablen" },
{ "java.launcher.opt.footer", " -cp n -classpath n A {0} separated list of directories, JAR archives,n and ZIP archives to search for class files.n -D=n set a system propertyn -verbose:[class|gc|jni]n enable verbose outputn -version print product version and exitn -version:n require the specified version to runn -showversion print product version and continuen -jre-restrict-search | -no-jre-restrict-searchn include/exclude user private JREs in the version searchn -? -help print this help messagen -X print help on non-standard optionsn -ea[:...|:]n -enableassertions[:...|:]n enable assertions with specified granularityn -da[:...|:]n -disableassertions[:...|:]n disable assertions with specified granularityn -esa | -enablesystemassertionsn enable system assertionsn -dsa | -disablesystemassertionsn disable system assertionsn -agentlib:[=]n load native agent library , e.g. -agentlib:hprofn see also, -agentlib:jdwp=help and -agentlib:hprof=helpn -agentpath:[=]n load native agent library by full pathnamen -javaagent:[=]n load Java programming language agent, see java.lang.instrumentn -splash:n show splash screen with specified imagenSee http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details." },
{ "java.launcher.opt.header", "Usage: {0} [-options] class [args...]n (to execute a class)n or {0} [-options] -jar jarfile [args...]n (to execute a jar file)nwhere options include:n" },
{ "java.launcher.opt.hotspot", " {0}t is a synonym for the "{1}" VM [deprecated]n" },
{ "java.launcher.opt.vmselect", " {0}t to select the "{1}" VMn" },
};
}
}

Java 웹 개발자의 학습 로드맵

Java 웹 개발자의 학습 로드맵

동영상 정보


강의감상평


이런 강의를 내가 취업하는 시점에 찾아보지 못하고 지금 본게 조금 아쉽다. 만약 내가 이 강의를 듣고 장기 과정의 흐름에 따라 진행 했으면 현재 보다 좋은 프로그램을 작성하는 개발자가 되었을거 같다는 뒤늦은 후회가 든다.

현 시점에서 내가 부족한 부분을 집어보자.

1. Java

자바는 2010년도부터 지금까지 꾸준히 내가 사용하는 언어다. 버전도 1.5에서 1.8까지 정식버전이 릴리즈가 되었고 조만간에 1.9가 릴리즈가 될 것 같다. 관심을 계속 주려고 하지만 현재 업무가 자바와 관련이 없어 뒷전으로 밀려있다. 아직도 볼게 많고 배울게 많이 남아 있다. 아마도 프로그래머라는 직업이 끝날때 까지는 계속 공부해야 될 것 같다.

2. Servlet

웹에서 사용하는 단순 요청 & 응답에 대한 처리만 하는 형식으로 공부하다보니 서블릿 생명주기 또는 구성을 제대로 공부한 적은 없는 것 같다.

3. 개발방법론

어떤 개발 방법론을 적용해서 현재 개발하고 있는지 모르고 과거에도 어떤 방법론을 썼는지 모르는걸 보면 개발 방법론에 대해서는 완전 무지하다.

4. OOP & 디자인 패턴

OOP. 디자인 패턴은 계속 공부를 해야 어느정도 알 수 있을 것 같다. 현시점에서는 감도 안온다.

5. Spring

2.5부터 시작했는데 정확하게 어떤 것을 하는지 모르겠다. 그냥 사용법 그리고 사용하면 편하다 정도로 생각하고 있다. 다시 한번 공부를 해야겠다.

6. ORM

Mybatis만 사용해서 Hibernate또는 JPA를 사용할 기회가 없었다. 개인적으로라도 공부해야겠다. 단순 사용법이 아닌 어떤 구조, 어떤 생각으로 이 프레임워크가 만들어졌는지를 생각하자.

7. 협업도구

지금 개발과정에서는 Mantis를 사용하고 있는데 단순 버그 트래킹 정도로만 사용하고 있다. 기회가 된다면 협업도구를 사용해봐야겠다.



단기과정


취업이 최우선이다.

취업을 먼저 한 후 장기 과정을 진행한다면 문제 없다. 그렇지만 장기과정을 따라하지 않고 현 위치에 머무르는 것은 한심하다.

[무조건 취업을 위해 준비해야하는 것들]

  • Java

  • eclipse

  • Spring framework

  • Mybatis

장기과정


  1. 통합개발도구 (eclipse, Intellij) [**]
  2. Java (최종까지 가장 중요하게 가지고 가야한다.) [*]
  3. Servlet + JSP [**]
  4. Build Tool [*]
    • Ant
    • Maven
    • Gradle
  5. 버전 관리 도구 [**]
    • SVN
    • CVS
    • Git
  6. 개발 방법론
    • TDD(테스트 주도 개발) [*]우선은 원칙대로 따라하고 자신에 맞게 변경하도록 한다.

      TDD가 내 몸에 익히면 야근 시간을 줄일 수 있다.

      TDD는 수영으로 치면 자세로 생각할 수 있도록 한다.

      처음 연습은 기본 기능으로 시작한다. String, Date등

  7. HTML & CSS & JAVASCRIPT [**]서버사이드 웹 개발자라면 기본적인 개념만 알고 있으면 된다.

    그렇지만 JAVASCRIPT를 언어적인 측면에서 접근한다면 도움이 된다.

  8. Model1 방식을 사용하여 웹 페이지 개발
    • Spring 또는 Struts가 나오게 된 계기를 알 수 있다.
    • MVC 패턴이 왜 편한지를 알 수 있다.
  9. OOP & 디자인 패턴 [*]
    • 다른 사람들과 다른 방식의 접근이 가능하다.
  10. Spring framework
    • 기본적인 사상들에 대해 먼저 습득하고 API를 확인한다(Dependency Injection)
  11. 생 JDBC 연결 및 중복 코드를 제거하면서 Spring-jdbc와 비교를 한다. [*]
    • Callback interface
    • Class 이념.
  12. JDBC
    • MyBatis
      • 학습 비용 낮고
      • SI에 사용
      • 테이블 정규화가 불가능
    • ORM(JPA, Hibernate) : 개인적으로 학습을 하여 인지를 하도록 한다.
      • 학습 비용 무지 높다
      • 솔루션, 자체 서비스 개발
      • 테이블 정규화가 가능
      • 생성산 향상이 크다
  13. 협업도구 [*]
    • JIRA
    • REDMINE

Apache POI 를 이용한 PPT to Image 결과

  1. 목표: HTML5 기반 PPT 문서 공유 프로그램
  2. 개발:
    1. PPT 슬라이드를 이미지 파일로 변경한다. (Apache POI, java.graphics)
    2. 발표자가 이미지를 변경하면 해당 이미지 파일명을 파라미터로 받아 파일 시스템에서 읽어 구독자에 전달한다.
  3. 결과
    1. Apache POI와 Java Graphic 을 이용한 이미지 전환 작업의 결과가 좋지 않다.

 

아래 ‘Original’ 이란 부분은 PPT를 이미지 캡처한 화면이고 ‘PPT to image’ 는 Apache POI를 통해 변환된 이미지를 캡처한 화면이다.
대략적인 의미 전달은 문제가 없겠지만 내용이 많은 PPT는 문제가 있을 것 같다.(실망) ^^

 

프로세스 스냅샷을 이용한 Java 어플리케이션 분석


사이트에 장애가 발생되면 처음 하는 일이  로그 분석입니다.

로그 분석 후 원인을 못 찾게 되면 다음 하는 일은 Thread Dump 입니다.

포스팅 내용은 ThreadDump 를 이용하여 무한 루프에 빠진 스레드 정보를 찾는 내용입니다.

하단 이미지는 제가 우분투 환경에서 가상으로 무한 루프를 돌리고 테스트를 한 내용입니다. (top command 이용)

coozplz@localhost:~$ top



전체 CPU 사용량은 노란색 테두리 부분을 합산한 내용으로 보시면 됩니다.(38+28)

붉게 표시된 부분의 CPU% 는 (CPU 사용량 / CPU Core 수) 를 하시면 됩니다. 저는 테스트 환경이 쿼드 코어에서 200%가 사용중이므로 즉 50%가 사용중입니다. (무한루프를 두번 만들었습니다.)

Thread 덤프를 통해 분석을 해야 하는데 Thread Dump에는 무한 루프에 빠졌다는 정보는 없습니다.


coozplz@localhost:~$ ps -mo pid,lwp,pcpu -C java

출력결과

20058     –  192

 

   – 20097  0.0

   – 20098  0.0

   – 20099  0.0

   – 20100  0.0

   – 20298  0.0

   – 20301  0.0

   – 20302  0.0

   – 20303  0.0

   – 20304  0.0

   – 20305  0.0

   – 20306  0.0

   – 20307  0.0

   – 20308  0.0

   – 20309  0.0

   – 20310  0.0

   – 20311  0.0

   – 20312  0.0

   – 20313  0.0

   – 20314  0.0

   – 20315  0.0

   – 20318 99.9

   – 20381 99.8

위의 내용중 20318’ 스레드와 ‘20381’ 스레드가 CPU 를 각각 99% 사용하고 있다는 정보가 확인 되었습니다.

‘20318’ HexString 값으로 변경하면 4f5e 라는 값이 나옵니다.

0x4f5e” 값으로 ThreadDump에서 분석을 해보면 어떤 스레드에서 무한 루프가 돌고 있는지 확인할 수 있습니다.


ThreadDump 명령어(Eclipse Console 또는 catalina.out 에 출력됨)

coozplz@localhost:~$ kill -3 20058


“Thread-118” daemon prio=10 tid=0x0000000001563000 nid=0x4f5e runnable [0x00007f0610f23000]

  java.lang.Thread.State: RUNNABLE

   at sun.nio.ch.FileDispatcherImpl.read0(Native Method)

   at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)

   at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)

   at sun.nio.ch.IOUtil.read(IOUtil.java:197)

   at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379)

   – locked <0x00000007db9e04f0> (a java.lang.Object)

   at coozplz.test.presense.NetworkSocket.read(NetworkSocket.java:153)

   at coozplz.test.presense.NetworkSocket.recvMessage(NetworkSocket.java:216)

   at coozplz.test.presense.NetworkSocket.recvMessageHead(NetworkSocket.java:355)

   at coozplz.test.presense.NetworkSocket.recvMessage(NetworkSocket.java:372)

   at coozplz.test.presense.ServerThread.run(ServerThread.java:33)


Dump내용중이 coozplz.test.presense.ServerThread 소스를 분석해보면 무한 루프를 찾을 수 있을 겁니다.