문서를 정리하다가 전에 정리된 문서에서 아래와 같은 내용이 있어서 포스팅 합니다..
단, 출처는 생각이 안 나네요. ^^;;

The following set of command line options are recommended for running high performance server applications:

-XX:+AggressiveOpts
-XX:+AggressiveHeap
-XX:ParallelGCThreads=2
-XX:InterpreterProfilePercentage=10
-XX:CompileThreshold=35000
-XX:OnStackReplacePercentage=80
-XX:PermSize=256m
-XX:MaxPermSize=256m
-XX:AllocatePrefetchStepSize=128
-XX:AllocatePrefetchStyle=2
-XX:AllocatePrefetchDistance=1024
-XX:+UseCompressedOops if heap size is less than 32GB
-Xmx, -Xms, -Xmn should have sufficiently large values.
(For example, -Xms6600m -Xmn5000m -Xmx6600m or -Xms20000m -Xmn18000m -Xmx20000m.)

저작자 표시

1. 다운로드 : http://java.sun.com/javase/downloads/index.jsp

Java SE Development Kit 6u21
* jdk-6u21-linux-i586.bin

2. 파일 퍼미션 변경 : chmod 700 jdk-6u21-linux-i586.bin

./jdk-6u21-linux-i586.bin 실행

jdk1.6.0_21

3. 풀린파일 복사 : sudo cp -r jdk1.6.0_21 /usr/local

4. 환경설정
/etc/profile 에 아래와 같이 기술을 하고 저장을 한다. 
JAVA_HOME=/usr/local/jdk1.6.0_21
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=$CLASSPATH:$JAVA_HOME/lib

5. 설정확인
source /etc/profile 을 통해서 환경설정을 확인한다.

6. 확인
java -version

저작자 표시

java signal handling

from java 2010/07/16 11:03
java에서 처리할 수 있는 signal과 그 내용입니다.
아래의 reference를 통해서 java에서 signal을 어떻게 처리하고, 새로운 signal을 등록할 수 있는 예제를 확인하실 수 있습니다.. 좋네요.. ^^

아래는 자바에서 지원되는 시그널과 내용입니다..

Signal Name

Description

SIGSEGV

Incorrect access to memory (write to inaccessible memory)

SIGILL

Illegal instruction (attempt to invoke a unknown machine instruction)

SIGFPE

Floating point exception (divide by zero)

SIGBUS

Bus error (attempt to address nonexistent memory location)

SIGSYS

Bad system call issued

SIGXCPU

CPU time limit exceeded (you've been running too long!)

SIGXFSZ

File size limit exceeded

SIGEMT

EMT instruction (AIX specific)

SIGABRT

Abnormal termination. The JVM raises this signal whenever it detects a JVM fault.

SIGINT

Interactive attention (CTRL-C). JVM will exit normally.

SIGTERM

Termination request. JVM will exit normally.

SIGHUP

Hang up. JVM will exit normally.

SIGUSR1

User defined. Used by some JVMs for internal control purposes.

SIGUSR2

User defined. Used by some JVMs for internal control purposes.

SIGQUIT

A quit signal for a terminal. JVM uses this for taking Java core dumps.

SIGBREAK

A break signal from a terminal. JVM uses this for taking Java core dumps.

SIGTRAP

Internal for use by dbx or ptrace. Used by some JVMs for internal control purposes.

SIGPIPE

A write to a pipe that is not being read. JVM ignores this.

No Name (40)

An AIX reserved signal. Used by the AIX JVM for internal control purposes.



OS별 지원되는 signal에 대한 내용입니다.

E.g. for Unix/Linux based platform the signals are SEGV, ILL, FPE, BUS, SYS, CPU, FSZ, ABRT, INT, TERM, HUP, USR1, QUIT, BREAK, TRAP, PIPE.

For Windows based platform, the signals are SEGV, ILL, FPE, ABRT, INT, TERM, BREAK.


reference
* http://www.ibm.com/developerworks/ibm/library/i-signalhandling/
* http://twit88.com/blog/2008/02/06/java-signal-handling/
* http://mimul.com/pebble/default/2009/07/19/1248007080000.html

저작자 표시

JDBC Type 별 내용

from java 2010/06/25 15:01
JDBC 1,2,3,4의 타입별 드라이버의 동작 방식과 설명입니다.
개인적으로는 가능한 JDBC4 타입으로 사용하는 것이 좋을듯 합니다.
아래내용은 http://www.precisejava.com/javaperf/j2ee/JDBC.htm#JDBC102 에서 참고를 하였습니다.

 Type
 Tier  Driver mechanism  Description
 1  Two  JDBC-ODBC  This driver converts JDBC calls to ODBC calls through JDBC-ODBC Bridge driver which in turn converts to database calls. Client requires ODBC libraries.
 2  Two  Native API - Partly - Java driver  This driver converts JDBC calls to database specific native calls. Client requires database specific libraries.
 3  Three  JDBC - Net -All Java driver  This driver passes calls to proxy server through network protocol which in turn converts to database calls and passes through database specific protocol. Client doesn't require any driver.
 4  Two  Native protocol - All - Java driver  This driver directly calls database. Client doesn't require any driver.

저작자 표시

'java' 카테고리의 다른 글

ubuntu(linux) server에서 java 설치하기  (0) 2010/07/20
java signal handling  (0) 2010/07/16
JDBC Type 별 내용  (0) 2010/06/25
외부 API를 JDK에서 내포한 경우, 최신의 외부 API 사용하기..  (0) 2010/06/22
#ifdef #ifndef in Java  (0) 2010/06/15
Tag // java, jdbc, jdbc type
외부 API들이 JDK에 많이 포함이 되어 있습니다.
http://java.sun.com/javase/6/docs/api/ 문서의 package들을 보시면, javax나 org로 시작하는 외부(?) API들을 보실 수가 있는데요.. JDK에서는 rt.jar에 포함되어 잇지만, 아래의 내용처럼, http://aploit.egloos.com/4806304 님의 블로그에 포스팅된 내용을 보시면, 최신의 외부 API를 사용하기 위해서 java.endorsed.dirs 옵션을 사용해서 해결하는 내용을 보실 수가 있습니다.
java로 작업을 하다보면 가끔씩 endorsed라는 디렉토리 명이 보였다. 사전을 찾아보면 배서하다, 추천하다, 양도하다 정도의 뜻이고, 사전적 의미가 아닌 java쪽에서는 어떤 의미로 사용되는지 몰랐다.

그냥 몰라도 별 문제는 없었고 해서 그냥 넘어갔었다.

그런데 의미를 확실히 알게 되었다. 

jdk1.4에는 JAXP1.2의 라이브러리들이 포함되어 있다. 대표적으로 org.w3c.dom의 패키지와 javax.xml의 패키지가 그것이다.

그런데 JAXP1.3, 1.4가 나왔고, jdk1.4의 환경에서 이들을 사용하고자 할 경우에 문제가 된다.
(참고로 JAXP1.3은 JDK5.0에 JAXP1.4는 JDK6.0에 포함되었다. 따라서 JDK5.0에서는 JAXP1.3 라이브러리를 따로 설치할 필요가 없다.) jdk는 1.4이고 어플리케이션에서 JAXP1.3을 설치했다고 하면(구체적인 jar 파일은 jaxp-api.jar와 jaxp-ri.jar이다) 같은 패키지 이름까지 같은 클래스가 2개 존재하게된다. 예를 들면 org.w3c.dom.Document이다. 하나는 rt.jar에 있는 것이고(버전은 JAXP1.2) 하나는 jaxp-api.jar에 있는 것이다(요 버전은 JAXP1.3). 원했던 것은 rt.jar의 것이 아닌 jaxp-api.jar의 것이 로딩되는것이였는데, 그렇게 동작하지 않는다. 설치한 라이브러리들 간의 로딩 순서는 classpath에 설정한 순서로 조절할 수 있지만, rt.jar는 설정 순서로 조절할 수 없다.

JDK 자체에 포함된 것이 아닌 설치한 라이브러리의 것을 로딩할때 사용하는 매커니즘이 바로 endorsed이다. "java.endorsed.dirs"라는 시스템 파라매터를 설정하면 된다. 다음과 같이 java를 실행할 때 jaxp-api.jar가 있는 디렉토리를 설정해 주면 된다.
java -Djava.endorsed.dirs=/path/where/jar/is ......
여기서 /path/where/jar/is 디렉토리는 jaxp-api.jar가 있는 곳이다.

TOMCAT의 경우 endorsed 매카니즘을 사용하고 있다. tomcat 4.1.39의 bin/setclasspath.bat에서 set JAVA_ENDORSED_DIRS=%BASEDIR%\common\endorsed 으로 endorsed 디렉토리를 설정하고 catalina.bat에서 다음과 같이 java 실행 시에 시스템 파라매터로 설정해 주고 있다.
%_EXECJAVA% ... -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%"

TOMCAT_HOME/common /endorsed에 가보면 xercesImpl.jar, xml-apis.jar가 있는데 이들은 JAXP 1.3의 것이다.


참 고로 클래스로딩의 매카니즘에서 "java." 로 시작하는 패키지의 클래스는 endorsed가 적용되지 않는다.


JAXP 에 대한 기타.
JAXP1.4가 설치되어 있다면 apache xalan의 다음과 같은 라이브러리들을 따로 설치할 필요 없다. JAXP안에 포함되어 있기 때문이다. 설치할 필요가 없는 것 보다는 쫑나는 상황을 피하기 위하여 설치해서는 안된다.
  serializer.jar
  xalan.jar
  xercesImpl.jar
  xml-apis.jar
  xsltc.jar

jaxp 는 sun의 glassfish 프로젝트에서 관리하고 있다.
저작자 표시

'java' 카테고리의 다른 글

java signal handling  (0) 2010/07/16
JDBC Type 별 내용  (0) 2010/06/25
외부 API를 JDK에서 내포한 경우, 최신의 외부 API 사용하기..  (0) 2010/06/22
#ifdef #ifndef in Java  (0) 2010/06/15
멀티쓰레드 프로그램 평가기준  (0) 2010/06/01

#ifdef #ifndef in Java

from java 2010/06/15 17:30
C구문의 전처리 구문인 #ifdef나 #ifndef같은 넘을 자바에서 사용하는 기본적인 내용입니다.
아래처럼 println이 DEF가 정의되었냐 혹은 DEBUG모드냐에 따라 달라지는 형태로 되어 있는 구문인 경우
public class IFDef{
public IFDef(){
 int i = 100;
 #ifdef DEF
  System.out.println("DEF");
 #else
  System.out.println("not DEF");
 #endif  
 }
}

아래처럼, 바꿔주면 됩니다.
private static final boolean def = false;

if (def) {
System.out.println("DEF");
} else {
System.out.println("not DEF");
}

Tag // ifdef, ifndef, java
유명한 Doug Lea 교수님의 멀티 쓰레드 프로그램의 평가기준에 대한 내용입니다.
"Java 언어로 배우는 디자인 패턴 입문 멀티 쓰레드편, Yuki Hiroshi" 의 Introduction 2 챕터에 나오는 내용입니다. 아래 내용의 출처는 http://ethdemor.springnote.com/pages/4349969 입니다.

아래의 기준들은 Doug Lea 분류를 기준으로 한 것이며, 이런 류의 평가 기준은 대개 상반(trade-off)되는 경우가 있습니다.

필 수
안전성 (safety) - 객체를 망가트리지 않을 것
 - "망가 진다"는 것은 비유적 표현으로 객체의 필드가 예상 외의 값을 취하는 것을 의미합니다.
 - 복수의 스레드가 이용해도 안전성이 유지되는 클래스를 스레드 세이프(thread-safe)한 클래스라고 하며 자바 API에는 명시되어 있습니다.
생존성 (liveness) - 필요한 처리가 이뤄질 것

옵 션
재사용성 (resuability) - 클래스를 다시 사용할 수 있는 것
 - 멀티 스레드 프로그램에서는 스레드 배타제어, 구조, 방침을 캡슐화 하는 것이 관건이며 Java SE 5.0의 java.util.concurrent 패키지에서 이를 잘 실현하고 있습니다.
 - 수행 능력(performance) - 고속 / 대량으로 처리할 수 있을 것
   - throughput - 단위 시간 당 처리 수
   - 응답성 (responsiveness) - 요청을 한 뒤 반응이 나타날 때까지 걸리는 시간
      - GUI 프로그램에서는 요청의 완료까지 걸리는 시간보다 처리 시작까지의 시간을 중요하게 생각하기도 합니다. 여기서 완료는 요청 -> 처리 -> 응답 까지의 시간이며 처 리 시작은 완료까지의 시간은 좀 더 걸리더라도 클라이언트에게 먼저 응답(~을 처리 중입니다 등)을 보내는 것을 말합니다.
      - 응답성이 높은 프로그램은 대기 시간(latency)이 짧다고 표현합니다.
   - 용량 (capacity) - 동시에 처리할 수 있는 수
   - 그 밖에 효율(efficiency), 확장성(scalability), 저하(degradation) 등이 있습니다.

평가기준 정리
멀티 스레드 시스템에서 안전성과 생존성이 지켜지지 않으면 제대로된 프로그램이라고 할 수 없습니다.
따라서 이 필수 조건을 만족한 상태에서 재사용성과 수행 능력을 어떻게 높이느냐가 관건이 되겠습니다.




'java' 카테고리의 다른 글

외부 API를 JDK에서 내포한 경우, 최신의 외부 API 사용하기..  (0) 2010/06/22
#ifdef #ifndef in Java  (0) 2010/06/15
멀티쓰레드 프로그램 평가기준  (0) 2010/06/01
간단한 SOAP 메시지 콜  (0) 2010/05/26
garbage collector  (0) 2010/04/29

간단한 SOAP 메시지 콜

from java 2010/05/26 15:34

아래처럼 SAAJ를 이용해서 SOAP 1.1 버전의 메소드를 호출해서 사용할 수 있다..
흠.. soap 보다는 json with rest 방식으로.. ^^;;

import java.io.ByteArrayInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.transform.dom.DOMSource;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class SoapClient implements IClient  {
 
 public static String soapActionUrl = "http://localhost/soap_http/test.asmx?op=GetPurchaseListWBC";
 
 private SoapClient(){};
 
 public static SoapClient newInstance() {
  return new SoapClient();
 }
 
 public static String getRequestMessage(int serviceCode, int uuid) {
  String query = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
   + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
   +  "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
   +  "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" >\n"
   //+  "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
   + " <soap:Body>\n"
   + "  <GetPurchaseListWBC xmlns=\"http://tempuri.org/\">\n"
      + "   <serviceCode>"+String.valueOf(serviceCode)+"</serviceCode>\n"
      + "    <uuid>"+ String.valueOf(uuid) +"</uuid>\n"
      + "   </GetPurchaseListWBC>\n"
   + " </soap:Body>\n"
   + "</soap:Envelope>";  
  return query;
 } 

 public SOAPMessage request(String soapActionUrl) throws Exception {
  Document doc = null;
  DocumentBuilder docBuilder = null;
  DOMSource domSource = null;
  SOAPConnection connection = null;
  
  SOAPMessage resultMessage = null;
  
  try {
   docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   doc = docBuilder.parse(new ByteArrayInputStream(getRequestMessage(1,2).getBytes()));
   domSource = new DOMSource(doc);
   
   SOAPMessage message = MessageFactory.newInstance().createMessage();  
   message.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "utf-8");
   message.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "true");
   message.getSOAPPart().setContent(domSource);
   message.getMimeHeaders().addHeader("SOAPAction", "http://tempuri.org/GetPurchaseListWBC");

   //String url = "http://localhost:1351/test.asmx?op=GetPurchaseListWBC";
   connection = SOAPConnectionFactory.newInstance().createConnection();
   resultMessage = connection.call(message, soapActionUrl);
   
   NodeList nodes = resultMessage.getSOAPBody().getChildNodes();
   
   for (int i = 0; i < nodes.getLength() ; i++) {
    Node node  = nodes.item(i);
    this.traverse(node);
   }

  } finally {
   connection.close();
  }
  
  return resultMessage;
 }
 
 public void traverse (Node node) {
  String nodeName = node.getNodeName();
     String nodeValue = node.getNodeValue();
    
     if(!"#text".equals(nodeName))
      System.out.println("<" + nodeName +">");

     if (node.hasChildNodes()) {
      NodeList nodes= node.getChildNodes();
      for (int i=0; i<nodes.getLength(); i++) {
       traverse (nodes.item(i));
      }
     } else {
      System.out.println(nodeValue);
     }
    
     if(!"#text".equals(nodeName))
      System.out.println ("</" + nodeName +">");
 } 
 
}

 

'java' 카테고리의 다른 글

#ifdef #ifndef in Java  (0) 2010/06/15
멀티쓰레드 프로그램 평가기준  (0) 2010/06/01
간단한 SOAP 메시지 콜  (0) 2010/05/26
garbage collector  (0) 2010/04/29
Circular Dependency Problem  (0) 2010/03/26
Tag // json, Rest, saaj, SOAP

garbage collector

from java 2010/04/29 13:51
자바의 Garbage Collector 이슈는 성능과 더불어 자원 해제라는 막중한 역할을 하기 때문에 중요하게 파악을 해 둬야 되는 내용입니다. 아래는 자바에서 지원하고 있는 Garbage Collector의 영역별 내용과 형태에 대한 내용입니다.

 - serial collector : young, old 영역, java 1.4 버전까지 default collector, single cpu만 사용
 - parallel collector : young 영역, java 5 버전부터 CPU 2, MEM 2G 이상(Hot Spot VM)일 경우 자동선택
 - parallel compacting collector : old 영역, option으로 지정해야 선택됨
 - cms(concurrent mark-sweep) collector : old 영역, option으로 지정해야 선택됨.
 -
G1 collector : young, old 영역,  1.6.0_14(NUMA 구조 지원, 1.6.0_18 버전에 성능에 대한 언급) 부터 사용이 가능합니다. option으로 지정해야 선택됨. “early access software”, 자바 비즈니스 라이선스를 구매하지 않은 고객은 사용을 하지 않는 것이 좋다고 합니다. Java 7버전을 타켓으로 하고 있다고 합니다.

Circular Dependency Problem

from java 2010/03/26 10:55
Circular Dependency의 해악은 개발을 하다보면 자연스럽게 알게 된다..
회사에서 개발하는 시스템중에 처음에는 그러하지 않았을 것인데, 아키텍처링을 할 수 없는 지금의 조직에서 자연스럽게 Circular Dependency를 걸어버렸다.. 일정이라는 최고의 명제 아래 말이다..
결국, 의존과 버전에 묶여서 뗄레야 뗄루 없는 커플처럼 되어 버린 기존의 시스템은 또다시 거대한 레거시로의 한발한발 다가서고 있다.. 정말로 뗄 수 없을 정도로 말이다..
누군가는 나서서 해결을 해야 겠지만, 누가 하겠는가.. 일정에 맞춰서 들어온 일 처리해야쥐..
결국 시스템의 모습은 직책자들의 마인드와 의지가 만들어 내는 우리의 현실이며, 이 모습은 좋은 개발팀 혹은 좋은 개발실은 아니라는 느낌을 지울 수 없다..

아래 Building속성에서의 Circular dependencies는 Warning이 아니라 Error 상황이 되어야 된다.
그래서, 위의 상황을 미연에 방지를 해야된다.. 단순한, 일정에 맞춰야 되서 Warning이 Default가 되어서는 안될 것이다.. ^^;;




'java' 카테고리의 다른 글

간단한 SOAP 메시지 콜  (0) 2010/05/26
garbage collector  (0) 2010/04/29
Circular Dependency Problem  (0) 2010/03/26
String의 공백은 trim()을 두번 호출해야 된다는 얘기에 대해서..  (0) 2010/03/04
소켓 연결 확인하기..  (0) 2010/03/03
어느 윗분께서 String의 trim()을 사용하면, 한번더 호출해야 정확하게 trim()이 된다고 말씀을 하셔서.. String 클래스의 trim()메쏘드를 살펴보니 아래와 같이 코딩이 되어 있네요..

    public String trim() {
        int len = count;
        int st = 0;
        int off = offset; /* avoid getfield opcode */
        char[] val = value; /* avoid getfield opcode */
     
        while ((st < len) && (val[off + st] <= ' ')) {
            st++;
        }
       
        while ((st < len) && (val[off + len - 1] <= ' ')) {
            len--;
        }
       
        return ((st > 0) || (len < count)) ? substring(st, len) : this;
    }

위 코드를 살펴보면, char[]의 ' ' <-- 공백을 앞쪽에서는 st를 뒤로 밀고, 뒤쪽에서는 len을 앞쪽으로 땅기면서 substring을 하게 되어 있네요..

위 코드를 보면 분명히 한번만 trim()하면 공백이 날라가네요.. ^^


소켓 연결 확인하기..

from java 2010/03/03 12:19
소켓을 기반으로 풀링을 하는 라이브러리를 개발할 시에 보통 소켓 클래스의 isconnected 라는 상태변수를 통해서 소켓의 연결상태를 확인할 수 있으나, 정확한 정보는 아니죠.. isconnected라는 변수는, 마지막 네트웍 이벤트를 처리한 소켓의 연결 상태이기 때문이죠.. 그래서 확실하게 풀을 관리하기 위해서는 네트웍 이벤트를 발생시켜 보는것이 Java에서는 해결책이라고 봐야 될듯 합니다. 
아래코드는 SocketChannel이 non-block인 상태를 기준으로, read 메쏘드를 호출했을 경우, 리턴값을 보고 상태를 확인하는 코드입니다.

int  readbytes = socketChannel.read(buf);

// 연결이 되어 있음..
if(readbytes == 0)
// 끊어졌네요.
if (readbytes == -1)
// 리시브된 데이타가 있네요..
if(readbytes >0)

소켓 풀링을 관리하는 매니저 클래스가 풀에 들어있는 소켓의 read 이벤트를 주기적으로 호출해보면, socket의 연결상태를 잘 유지할 수 있을 것입니다. ^^
보통 객체에 대한 고유값으로 hashCode() 메쏘드를 많이 사용하는데요.....
hashCode()는 Override가 가능하기 때문에, 고유값이 아닐경우가 잠재적으로 존재하게 됩니다.
그런 문제상황(?)을 해결할 수 있도록 System.identityHashCode(Object x)라는 메쏘드가 존재하고 있네요..

기존의 아래와 같은 방식을..
System.out.println(aa.hashCode());


이렇게 바꿔야 겠네요.. ^^
  System.out.println(System.identityHashCode(aa));
     System.out.println(System.identityHashCode(null)); <-- null일경우는 0이네요.. ^^

     

자바 메모리 구조

from java 2010/01/27 18:04

MemoryUsage 클래스를 통해서 메모리에 대한 정보를 모니터링 해보면 아래의 결과를 볼수 있습니다.

USAGE(Code Cache) init = 163840(160K) used = 535168(522K) committed = 557056(544K) max = 33554432(32768K)
USAGE(Eden Space) init = 917504(896K) used = 807560(788K) committed = 917504(896K) max = 4194304(4096K)
USAGE(Survivor Space) init = 65536(64K) used = 0(0K) committed = 65536(64K) max = 458752(448K)
USAGE(Tenured Gen) init = 4194304(4096K) used = 0(0K) committed = 4194304(4096K) max = 61997056(60544K)
USAGE(Perm Gen) init = 12582912(12288K) used = 699408(683K) committed = 12582912(12288K) max = 67108864(65536K)
USAGE(Perm Gen [shared-ro]) init = 8388608(8192K) used = 5344032(5218K) committed = 8388608(8192K) max = 8388608(8192K)
USAGE(Perm Gen [shared-rw]) init = 12582912(12288K) used = 6771000(6612K) committed = 12582912(12288K) max = 12582912(12288K)



위 내용을 보면, 대략 7개의 메모리 영역이 있네요.. 메모리 영역에 대해서 찾아보니... 어느 성능관련 엔지니어님의 블로그에 아래와 같은 설명이 있네요. ^^ 

Code Cache: contains memory used for compilation and storage of native code
Eden Space: pool from which memory is initially allocated for most objects
Survivor Space: pool containing objects that have survived Eden space garbage collection
Tenured Gen: pool containing long-lived objects
Perm Gen: contains reflective data of the JVM itself, including class and memory objects
Perm Gen [shared-ro]: read-only reflective data
Perm Gen [shared-rw]: read-write reflective data



 

// second
long second = TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS);
// millisecond
long second = TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS);

며칠전에 사석에서 상사분이 Tomcat은 IIS에 비해서 connection을 적게 받고, 그로 인해서 성능이 떨어진다고 말씀을 하시네요.. ^^;; 서비스의 전체적인 성능 문제가 Java 및 Tomcat에 기인을 하고 있다고 생각을 하는것 같아서.. 같은 개발자 입장에서 타 분야에 대한 경험으로 얘기하는게 아니라 말 그대로 선입견(Overlapped I/O의 성능만 말씀하시네 ^^;;)으로 얘기를 하는 듯한 느낌이었습니다.
 
서비스의 성능저하는 Tomcat이나 IIS보다는 그 위에 올라가는 어플이 더 성능에 영향을 미칠텐데 말입니다. ^^;;
그래서 IIS 기반의 ASP.NET 서비스들이 주로 사용하는 DataSet에 대해서 Serialize 데이타에 대해서 살펴보았습니다. 비교는 단순하게 Java , C# Object, C# DataSet으로 10개의 리스트를 가지는 모델을 가지고 하였습니다.

아래의 코드로 Java에서의 Object Array, C#에서의 Object Array, C#에서의 DataSet의 Serialize된 객체의 사이즈를 알수 있겠습니다. 단순하게 Serialize하는 속도 및 Deserialize하는 속도는 같다고 가정을 합니다.

* Java
SerializeModel.java

import java.util.ArrayList;
import java.io.Serializable;

public class SerializeModel implements Serializable {
 private static final long serialVersionUID = -7168303693593724718L;
 private int count = 0;
 private String name = null;
 private String address = null;
 private ArrayList<SerializeModel> models = new ArrayList<SerializeModel>();
 
 public SerializeModel(int count, String name, String address) {
  this.count = count;
  this.name = name;
  this.address = address;
 }

 public void addModel(SerializeModel model) {
  this.models.add(model);
 }
}



SerializeTest.java

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerializeTest {
 
 public static void main(String[] args) throws Exception {
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("java.ser"));
  
  SerializeModel model = new SerializeModel(0, "a", "b");
  
  for(int i=0; i<10; i++) {
   model.addModel(new SerializeModel(i, i +" name", i +" address"));
  }
  
  out.writeObject(model);
  out.flush();
  out.close();
 }
}



* C#

SerializeModel.cs

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Collections;
using System.Collections.Generic;

namespace SerialzieTest
{
    [Serializable()]
    class SerializeModel
    {
     private int count = 0;
     private String name = null;
     private String address = null;
     private ArrayList models = new ArrayList();
     
     public SerializeModel(int count, String name, String address) {
      this.count = count;
      this.name = name;
      this.address = address;
     }

     public void AddModel(SerializeModel model) {
      this.models.Add(model);
     }
    }
}



SerializeModelDataSet.cs

using System;
using System.Data;
using System.Collections.Generic;
using System.Text;

namespace SerialzieTest
{
    [Serializable]
    class SerializeModelDataSet
    {
        private DataSet dataSet = new DataSet();
        public void AddDataTable(DataTable table) {
            this.dataSet.Tables.Add(table);
        }

        public DataSet GetDataSet()
        {
            return this.dataSet;
        }
    }
}



Program.cs

using System;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerialzieTest
{
    class Program
    {
        static void SerializeObjectArray()
        {
            Stream stream = File.Open("csharp.ser", FileMode.Create);
            BinaryFormatter bformat = new BinaryFormatter();

            SerializeModel model = new SerializeModel(0, "a", "b");
            for (int i = 0; i < 10; i++)
            {
                model.AddModel(new SerializeModel(i, i + " name", i + " address"));
            }
            bformat.Serialize(stream, model);
            stream.Flush();
            stream.Close();
        }

        static void SerializeDataSet()
        {
            Stream stream = File.Open("csharp_dataset.ser", FileMode.Create);
            BinaryFormatter bformat = new BinaryFormatter();
            SerializeModelDataSet model = new SerializeModelDataSet();

            DataTable table = new DataTable();
            DataColumn itemCount = new DataColumn("count", Type.GetType("System.Int32"));
            DataColumn itemName = new DataColumn("name", Type.GetType("System.String"));
            DataColumn itemAddress = new DataColumn("address", Type.GetType("System.String"));

            table.Columns.Add(itemCount);
            table.Columns.Add(itemName);
            table.Columns.Add(itemAddress);

            DataRow row;

            for (int i = 0; i < 10; i++)
            {
                row = table.NewRow();
                row["count"] = i;
                row["name"] = i + " name";
                row["address"] = i + " address";
                table.Rows.Add(row);
            }
            model.AddDataTable(table);
            bformat.Serialize(stream, model);
            stream.Flush();
            stream.Close();
        }

        static void Main(string[] args)
        {
            SerializeObjectArray();
            SerializeDataSet();
        }
    }
}




위의 코드를 통해서 저장한 Object의 사이즈는 아래와 같습니다.

Java(Object ArrayList) : 665 byte
C#(Object ArrayList) : 1035 byte
C#(DataSet) : 3000 byte

네트웍을 통해서 전송되는 Data의 크기는 성능에 중요한 영향을 미치지요..
실제로 성능을 높이기 위해서 Socket Buffer의 사이즈를 적당히 줄이는 것도 팁으로 나와 있구요..

위 상황에서 C#의 DataSet은 더욱 멋진 모습을 보여줍니다.
아래의 화면처럼 말이죠.. 아래의 화면은 Hex Viewer로 본 화면입니다.


Object의 Serialize는 C#이 version, locale 및 가비지(?) 데이타로 인해서 좀 더 크게 나옵니다.
그리고, 중간의 DataSet은 Serialize된 내용에 xml의 형태로 저장을 하고 있네요.. 결국 DataSet이 데이타로 네트웍을 통해서 전송이 되면, 사이즈보다는 Serialize/Deserialize할때의 String연산(XML 데이타 리드)이 성능저하의 주범이 되지 않을까요? 결국, Tomcat, IIS가 중요한게 아니라 C#에서 널리 쓰이는 DataSet을 많이 사용하고 있는 IIS기반의 ASP.NET 어플들의 서비스가 더 성능이 안 좋을 거라는 생각이 드네요... ^^;;;;

Exception StackTrace to a String

from java 2009/11/24 11:15

Exception이 발생하게 되면 Call Stack에 대한 내용을 저장을 해야 디버깅을 할 수 있다.. 

구글링을 하다보니, Save Exception StackTrace to a String 에서 Exception을 String으로 변환하는 쉬운 예를 보여주고 있네요. 

몇개의 예가 있긴 한데, 아래의 코드가 간결하고 좋네요.. ^^

public static String getException(Exception e )
{
StringWriter w = new StringWriter();
e.printStackTrace(new PrintWriter(w));
return w.toString();
}
Tag // exception, java
java.util.Timer를 Logging 라이브러리에서 사용을 하다보니, Logging라이브러리를 사용하고 있는 로깅서버에서 addShutdownHook() 메쏘드를 통해서 등록된 쓰레드가 실행이 안 된다.. ^^;;

Java API 문서를 보면, 아래와 같은 내용이 있습니다.
Timer 객체의 마지막 라이브 참조가 종료해, 미처리의 태스크가 모두 실행되면, 타이머의 태스크 실행 thread도 동시에 완료해, 가비지 컬렉트됩니다. 다만, 이것에는 한없고 긴 시간이 걸리는 경우가 있습니다. 디폴트에서는 태스크의 실행 thread는 「demon thread」로서는 실행되지 않기 때문에 어플리케이션이 종료하지 않게 할 수 있습니다. 타이머의 태스크 실행 thread를 즉시 완료시키는 경우, 호출측은 타이머의 cancel 메서드를 호출할 필요가 있습니다.
흠... 그렇군요.. 종료가 안되더군요... ^^;;
그래서, Timer() 대신 daemon으로 Timer객체를 생성하면, addShutdownHook() 메쏘드에 등록된 쓰레드의 run() 메쏘드가 잘 동작하더군요.. ^^;;

Timer ()
          새로운 타이머를 작성합니다.

Timer (boolean isDaemon)
          demon로서 실행되도록 지정할 수 있는 관련 thread를 가지는 새로운 타이머가 작성됩니다.

GMail의 ID/PW만 있으면 메일을 쉽게 보낼 수가 있네요. ^^
아래는 http://www.easemarry.com/blog/java-use-gmail-account-to-send-mail/ 의 코드를 제가 사용하기 위해서 살짝 바꿨습니다. ^^

package net.sjava.email.test;

import java.security.Security;
import java.util.Date;
import java.util.Properties;  
 
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;  

public class GMailSenderTest {

    private static final String SSLFACTORY = "javax.net.ssl.SSLSocketFactory";
    private static final String USERID = "id@gmail.com";
    private static final String PASSWORD = "pw";
    private static Properties properties = System.getProperties();
   
    static {
        properties.setProperty("mail.smtp.host", "smtp.gmail.com");
        properties.setProperty("mail.smtp.socketFactory.class", SSLFACTORY);
        properties.setProperty("mail.smtp.socketFactory.fallback", "false");
        properties.setProperty("mail.smtp.port", "465");
        properties.setProperty("mail.smtp.socketFactory.port", "465");
        properties.put("mail.smtp.auth", "true");
    }
   
    //
    public Message getMessage(Session session, String to, String title, String content) throws MessagingException {
        Message msg = new MimeMessage(session);     
        msg.setFrom(new InternetAddress(to));
        msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false));
        msg.setSubject(title);
        msg.setText(content);
        msg.setSentDate(new Date());
        
        return msg;
    }
   
    //
    public void send(String to, String title, String content) {
        
        try {
            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());            
            Session session = Session.getDefaultInstance(properties, new Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(USERID, PASSWORD);
                }
            });            
            Transport.send(this.getMessage(session, to, title, content));
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
   
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GMailSenderTest sender = new GMailSenderTest();
        sender.send("skidboy@daum.net", "헐", "내용");
    }
}



Tag // Email, gmail, java
흠..
며칠전에 회사에서 synchronized keyword로 얘기를 하다가 그냥 말았다..
메쏘드(임계영역)만 lock이 걸리는 형태로 알고 있으셔서 흠..

아래 코드를 돌려보자..
아래와 같은 메세지를 뿌려준다.. 즉, 아래 코드의 singleton 객체에 대한 락은 묵시적으로 Monitor객체를 취득하고 메쏘드를 빠져나가면 반환하는 구조로 되어 있는데 말이다..

따라서, 메쏘드 01에 진입을 하게 되면, 02는 01이 완료될때까지 대기를 하다가 01이 완료되면 02메쏘드가 불리게 되는 그런 구조이다..

print01 call
print01 진입
print02 call
print01
print02 진입
print02

package net.sjava.test.sync;

public class SyncTestMain {
   
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Thread(){
            public void run() {
                SingleTon instance = SingleTon.getInstance();
                System.out.println("print01 call");
                instance.print01();           
            }
        }.start();
       
        new Thread(){
            public void run() {
                SingleTon instance = SingleTon.getInstance();
                System.out.println("print02 call");
                instance.print02();           
            }
        }.start();
   
    }
}

package net.sjava.test.sync;

public class SingleTon {

    private static SingleTon instance = new SingleTon();
   
    private SingleTon() {
       
    }
   
    public static SingleTon getInstance() {
        return instance;
    }
   
    public synchronized void print01() {
        System.out.println("print01 진입");
        try {
            Thread.sleep(5000);
            System.out.println("print01");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
   
    public synchronized void print02() {   
        System.out.println("print02 진입");
        try {
            Thread.sleep(1000);
            System.out.println("print02");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

* reference
 - http://iilii.egloos.com/4071694


한글 문자 확인하기..

from java 2009/09/01 16:59

캐릭터가 한글인지, 문자열에 한글이 포함되어 있는지를 체크할 수 있는 내용입니다.
reference는 http://blog.daum.net/kjourney/6386029 입니다.

public class KoreanCharacterExistTest {

 /**
  * 한글 문자를 체크한다.
  *
  * @param ch
  * @return
  */
 public static boolean isKoreanCharacter(char ch) {
  String block = Character.UnicodeBlock.of(ch).toString();
  
  if(block.equals("HANGUL_JAMO") || block.equals("HANGUL_SYLLABLES") || block.equals("HANGUL_COMPATIBILITY_JAMO"))
   return true;
  
  return false;
 }
 
 /**
  * 한글 문자 포함여부를 체크한다.
  * @param str
  * @return
  */
 public static boolean isKoreanCharacterContained(String str){
  if(str == null || str.length() < 1)
   return false;
   
  for(int i=0; i< str.length(); i++){
   if(isKoreanCharacter(str.charAt(i)))
    return true;   
     }
  
  return false;
 }
 
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  System.out.println("sldkflkl - " + isKoreanCharacterContained("sldkflkl"));
     System.out.println("가나다라 - " + isKoreanCharacterContained("가나다라"));
     System.out.println("sldk가나 - " + isKoreanCharacterContained("sldk가나"));
     System.out.println("가나kflkl - " +isKoreanCharacterContained("가나kflkl"));
 }
}


Tag // java, 한글
java.util.Timer에 대한 Good Practice에 대한 내용으로 http://tech.puredanger.com 블로그에 있는 내용입니다.
Two good rules of thumb for the use of Timer are:

1. Always start your Timer as a daemon thread. By default, new Timer() and other constructors use a non-daemon thread which means that the Timer will stick around forever and prevent shutdown. That may be what you expect, but I doubt it.
2. Always name your Timer. That name is attached to the Timer background thread. That way, if you do something dumb, and look at a thread dump, it will be exceedingly obvious when you’ve started 500 FooBarCleanupThreads.
위의 내용을 요약하면, "가능한 데몬 쓰레드로 실행을 시키고 Timer에 이름을 줘서 쓰레드 덤프등을 통해서 분명하게 디버깅을 할 수 있도록 하자"로 파악을 하였습니다.

그리고, http://tech.puredanger.com 블로그에 있는 내용의 댓글로 Timer를 사용하지 말고, ScheduledExecutorService를 사용하는 것이 좋다고 하네요.. 이유는 Timer(싱글 쓰레드)가 가지는 원천적인 문제를 해결하기 위해서라고 하네요.. 원본 내용이 짧기 때문에 읽어보세요.

ScheduledExecutorService의 간단한 예제는 http://www.java2s.com/Code/Java/Threads/Java1550newfeatureThreadSchedule.htm 를 참고하세요.. ^^

java.util.Timer의 schedule vs scheduleAtFixedRate에 대한 내용도 참고해 보세요. ^^

파일에 로그를 남긴다거나 데이타를 쓰기위해서 java.io.File 객체를 써도 되지만, 성능을 위해서 BufferedWriter를 많이 이용하게 됩니다. 특히 로그 라이브러리나 로깅 유틸리티 클래스에서 거의 대부분 사용할 것으로 생각이 드네요..
그래서, 생각한 방법이 BufferedWriter를 캐싱하는 것입니다.

아래 코드의 shutdown() 메쏘드는 ShudownHooking으로 등록된 쓰레드가 프로세스가 종료되면 호출을 해서 버퍼에 남아있는 내용을 기록하도록 되어 있습니다.

BufferedWriterCacheUtility.java
package net.sjava.io.test;

import java.util.Map;
import java.util.LinkedHashMap;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.util.Iterator;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BufferedWriterCacheUtility {
   
    /** max cache size */
    private static final int size = 10;
    /** lock */
    private static Lock lock = new ReentrantLock();
   
    /** initialized lru cache */
    private static Map<String, BufferedWriter> cache = new LinkedHashMap<String, BufferedWriter> (size, .75F, true) {
        private static final long serialVersionUID = 1L;
        protected boolean removeEldestEntry(Map.Entry<String, BufferedWriter> eldest) {
            if(size() > size) {
                try {
                    // flush and close before deleted
                    eldest.getValue().close();
                } catch(java.io.IOException e) {
                    // ignore
                }
            }
           
            return size() > size;  
        }
    };

   
    /**
     *
     * @param fileName
     * @return
     */
    public static BufferedWriter createBufferedWriter(String fileName) {
        lock.lock();
        try{
            if(cache.containsKey(fileName))
                return cache.get(fileName);
       
            return new BufferedWriter(new FileWriter(fileName, true), 1024);
        } catch(java.io.IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            lock.unlock();
        }
    }
   
    /**
     *
     * @param fileName
     * @param writer
     */
    public static void close(String fileName, BufferedWriter writer) {
        lock.lock();
        try {
            cache.put(fileName, writer);
        } finally {
            lock.unlock();
        }
    }
   
    /** shutdown hooking thread call */
    public static void shutdown() {
        // 아래의 코드를 통해서 종료가 되더라도 다 flush 하고 종료한다.     
        Iterator<BufferedWriter> iter = null;
        try {
            iter = cache.values().iterator();
            while (iter.hasNext()){
                iter.next().close();
                //iter.next().close();
            }
           
        }catch(java.io.IOException e) {
            e.printStackTrace();
        }
    }
}

아래 코드는 BufferedWriter를 사용하는 방법에 대한 3가지의 예입니다.
물론 2번째(bwriter02)를 사용하는 예가 가장 빠르겠지만, 3번째 예는 여러개의 fileName을 가지고 file write를 하기에 가장 좋은 방법으로 생각이 듭니다.

BufferedWriterTest.java
package net.sjava.io.test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BufferedWriterTest {

    private static SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS");

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //load();
               
        System.out.println("s - " + format.format(new Date()));
       
        BufferedWriter bwriter01 = null;
        try {
            for(int i=0; i < 100000; i++) {
                bwriter01 = new BufferedWriter(new FileWriter("c:\\test01.txt", true), 1024);
                bwriter01.write("aaaaaaaaaaaaccccccccccccccccccaaa");
                bwriter01.newLine();
                bwriter01.flush();
                bwriter01.close();
            }
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
        System.out.println("e - " + format.format(new Date()));
        
        System.out.println("s - " + format.format(new Date()));
        BufferedWriter bwriter02 = null;
        try {
            bwriter02 = new BufferedWriter(new FileWriter("c:\\test02.txt", true), 1024);
            for(int i=0; i < 100000; i++) {
                bwriter02.write("aaaaaaaaaaaaccccccccccccccccccaaa");
                bwriter02.newLine();
            }
            bwriter02.flush();
            bwriter02.close();
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
        System.out.println("e - " + format.format(new Date()));        
        
        // 날짜별 혹은 서비스별로 로깅을 해야 될경우
        System.out.println("s - " + format.format(new Date()));
        BufferedWriter bwriter03 = null;
        try {
            for(int i=0; i < 100000; i++) {
                bwriter03 = BufferedWriterCacheUtility.createBufferedWriter("c:\\test03.txt");   
                bwriter03.write("aaaaaaaaaaaaccccccccccccccccccaaa");
                bwriter03.newLine();
                BufferedWriterCacheUtility.close("c:\\test03.txt", bwriter03);
            }
        } catch(java.io.IOException e) {
            e.printStackTrace();
        }
       
        System.out.println("e - " + format.format(new Date()));
    }

}

여담이지만, java.io.BufferedWriter 소스를 보면, write시에 lock을 잡고 씁니다.
아래와 같은 코드를 많이 사용하지만요..
lock.lock();
try {
 bWriter.write("000000000000000");
}catch(java.io.IOException e) {
  e.printStackTrace();
} finally {
  lock.unlock();
}
위 코드보다는 아래코드를 사용하는게 맞겠습니다.
try {
 bWriter.write("000000000000000");
}catch(java.io.IOException e) {
  e.printStackTrace();
}

일정 시간이 지난뒤에 실행을 시켜주는 Utility 클래스로 Java에서는 Timer 클래스가 있습니다.
Timer 클래스를 사용하다 보니, 동일한 기능의 다른 메쏘드(schedule, scheduleAtFixedRate)가 있어서 차이점에 대해서 살펴보았습니다.

Java API에서는 Timer의 schedule과 scheduleAtFixedRate 메쏘드 둘다,
지정한 태스크가 지정한 지연의 후에 시작되어 「고정 빈도 실행」을 반복하도록 스케줄 합니다. 그 후는 지정한 기간과는 별도로 거의 일정한 간격으로 실행됩니다.

Java API의 schedule 메쏘드에 대한 내용은,
고정 지연 실행에서는 전의 실행의 실제의 실행 시간을 기준으로의해 각각의 실행이 스케줄 됩니다. 어떠한 이유로써 실행이 지연 했을 경우 (가비지 컬렉션, 그 외의 백그라운드 작업 등), 그 후의 실행도 지연 됩니다. 최종적으로 실행의 빈도는 보통, 지정한 기간의 대응하는 빈도보다 약간 늦어집니다 (기본이 되는 Object.wait(long)를 지지하고 있는 시스템 클록이 정확이라고 하는 전제로).

Java API의 scheduleAtFixedRate 메쏘드에 대한 내용은,
고정 빈도 실행에서는 최초의 실행의 스케줄 된 실행 시간을 기준으로의해 각각의 실행이 스케줄 됩니다. 어떠한 이유로써 실행이 지연 했을 경우 (가비지 컬렉션 또는 그 외의 백그라운드 작업 등), 「지연을 되찾는다」위해 2개 이상의 실행이 연속해 행해집니다. 최종적으로 실행의 빈도는 지정한 기간의 대응하는 빈도와 같게 됩니다 (기본이 되는 Object.wait(long)를 지지하고 있는 시스템 클록이 정확이라고 하는 전제로).

따라서, 위 내용을 토대로 schedule되는 job의 수행 횟수 또는 비교적 critical한 수행을 해야 되는 경우에는  scheduleAtFixedRate 메쏘드를 사용해야 될것 같습니다.

예제코드
TimerTest.java
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Timer timer = new Timer("test");
        timer.schedule(new TimerTask() {
            public void run(){
                try {
                        Thread.sleep(2000);
                        System.out.println("- schedule job run at " + new java.util.Date());
                    } catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }, 0, 1000 * 1); // 10 초
       
       
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run(){
                try {
                        Thread.sleep(2000);
                        System.out.println("- scheduleatfixedrate run at " + new java.util.Date());
                    } catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }, 0, 1000 * 1); // 10 초
    }
}

예제화면


그리고, Timer에 schedule된 Task가 중간에 exception이 발생하면, 그 Timer의 모든 task들이 실행이 안 되네요. ^^;;

* reference
- http://rijusnotes.blogspot.com/2009/06/how-to-implement-scheduler-in-java.html
- http://blog.naver.com/saojung50/120050451007
문자열을 비교하는 코드는 프로그램을 개발하다보면 많이 사용하게 됩니다.
보통 아래처럼 많이 비교하게 됩니다. 하지만, 잠재적으로 문제상황을 내재를 하고 있죵..

public void compareType(Stirng type)
{
    if(type.equals("AAA"))
        ...
    else if(type.equals("BBB"))
        ...
    else if(type.equals("CCC"))
        ...
}

위의 코드를 아래처럼 바꾸게 되면, 잠재적인 NullPointerException 상황을 피할수 있네요.

public void compareType(Stirng type)
{
    if("AAA".equals(type))
        ...
    else if("BBB".equals(type))
        ...
    else if("CCC".equals(type))
        ...
}

위 내용은 http://entireboy.egloos.com/2411775 에서 참고를 하였습니다.
Tag // equals, java