메모리 덤프

java core 2007. 8. 21. 00:48

 

linux(redhat)에서 jdk 1.5를 쓰는 상황에서 메모리 덤프할때.

 

java 실행시 추가

-Xrunhprof:file=heapdump.hprof,format=b,depth=15

= format=b : binary로 저장하여 jhat을 사용하여 분석할 수 있도록

= depth=15, 원래 디폴트값은 4인데, 도저히 쓸 수 알아보기 힘들어서 스택을 깊게 볼 수 있도록 처리

 

메모리 덤프

kill -3

하면 됨 (메모리 덤프뜨면, restart 다시 할 것)

 

 

jhat (힙 애널라이저 툴)를 다운로드

https://hat.dev.java.net/

 

압축을 풀어 hat.jar를 실행하고.

 

java -Xms600m -Xmx600m -jar hat.jar -port 7002 heapdump.hprof

사용하여.

 

웹브라우져에서 localhost:7002 요청을 하면.. 메모리 덤프 내용을 볼 수 있다..

 

 

 

 

Posted by '김용환'
,

JVM 튜닝 방법

 

첨부파일 설명

 

BEA 특별기고 - JVM GC 와 메모리 튜닝 (조병욱)

 

OutOfMemory

 

JVM 메모리 할당공식 레포트

 

 

 

 

 

 

'java core' 카테고리의 다른 글

[윈도우즈 용] 스레드 덤프 채취하는 툴  (0) 2007.08.21
메모리 덤프  (0) 2007.08.21
ThreadLocal 클래스  (0) 2007.08.02
자바 로그 관련  (0) 2007.01.02
shift에 대해서.  (0) 2006.11.16
Posted by '김용환'
,

ThreadLocal 클래스

java core 2007. 8. 2. 12:10

webwork의 ActionContext에 private static ThreadLocal 변수가 있었다.

내부적으로 map을 이용하여 thread별로 thread 변수를 가지게 하는 구조로 되어 있었다. 자세한 것은 api와 소스를 참조하면 될 것 같다.

 

글 참조할 것~

 

http://kr.sun.com/developers/techtips/core_12_12.html

쓰레드의 상태정보를 저장할 때 사용되는 THREADLOCAL 변수들

특정 쓰레드의 스코프에서 사용할 로컬변수가 필요했던 적이 있는가? 이때에 각각의 쓰레드는 고유의 스토리지를 갖고 하나의 쓰레드는 다른 쓰레드의 상태 정보를 액세스하는 것이 불가능할 것이다. 표준 라이브러리는 이러한 요구를 가능케 하는 ThreadLocalInheritableThreadLocal, 2개의 클래스들을 제공하고 있다.

클래스들이 사용되고 있는 예를 보자.

  import java.util.Random;
public class ThreadLocalTest1 {
 // Define/create thread local variable
 static ThreadLocal threadLocal = new ThreadLocal();
 // Create class variable
 static volatile int counter = 0;
 // For random number generation
 static Random random = new Random();
 // Displays thread local variable, counter,
 // and thread name
 private static void displayValues() {
  System.out.println(threadLocal.get() + "\t" + counter + "\t"
    + Thread.currentThread().getName());
 }
 public static void main(String args[]) {
  // Each thread increments counter
  // Displays variable info
  // And sleeps for the random amount of time
  // Before displaying info again
  Runnable runner = new Runnable() {
   public void run() {
    synchronized (ThreadLocalTest1.class) {
     counter++;
    }
    threadLocal.set(new Integer(random.nextInt(1000)));
    displayValues();
    try {
     Thread.currentThread().sleep(
       ((Integer) threadLocal.get()).intValue());
     displayValues();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  };
  // Increment counter, access thread local from
  // a different thread, and display the values
  //synchronized (ThreadLocalTest1.class) {
   counter++;
  //}
  threadLocal.set(new Integer(random.nextInt(1000)));
  displayValues();
  // Here's where the other threads
  // are actually created
  for (int i = 0; i < 5; i++) {
   Thread t = new Thread(runner);
   t.start();
  }
 }
}

ThreadLocal1클래스는 2개의 정적 변수를 갖는다. common integer primitive 타입인 counterThreadLocal 타입의 변수인 threadLocal이 바로 그것이다. 일반적으로 클래스 변수를 사용하면, 해당 클래스의 모든 인스턴스들은 동일한 값을 공유하게 된다. 하지만 ThreadLocal의 경우는 다르다. ThreadLocal클래스를 사용하면 동일한 쓰레드내에서만 같은 정적 정보를 공유하게 되는 반면, 서로 다른 쓰레드에서 ThreadLocal 클래스를 사용하면 서로 다른 정적 정보를 갖게 된다.

ThreadLocal1에서, ThreadLocal 인스턴스는 0에서 999사이의 랜덤한 숫자로 초기화된다.

   threadLocal.set(     new Integer(random.nextInt(1000)));

다수의 서로 다른 쓰레드가 생성되었다. 각각의 쓰레드 별로, 2개의 클래스 변수에 대한 상태 정보가 디스플레이된다. 그러면 이 쓰레드는 ThreadLocal이 랜덤한 숫자로 지정한 만큼의 밀리초동안 휴면한다. 이후, 2개의 클래스 변수의 상태 정보가 다시 디스플레이되고, ThreadLocal1를 실행하면, 아래와 같은 출력값을 보게 된다.

   828     1       main   371     2       Thread-0   744     3       Thread-1   734     4       Thread-2   189     5       Thread-3   790     6       Thread-4   189     6       Thread-3   371     6       Thread-0   734     6       Thread-2   744     6       Thread-1   790     6       Thread-4

여기에서 주목할 것은 세번째 컬럼에서 쓰레드의 이름이 같을 때를 살펴보면, 첫번째 컬럼의 ThreadLocal 변수값이 프로그램이 실행되는 동안 같은 상태를 유지한다는 것이다. 반면, 두번째 컬럼에서 보이는 정적 변수는 프로그램이 실행하는 동안 변화하고, 기존 쓰레드를 재검토할 때 그 바뀐 값을 유지한다. 이것이 바로 적절하게 동기화된 정적 변수가 해야 하는 일이다.

프로그램을 실행할 때마다 모든 컬럼에는 각기 다른 값이 생성되기 때문에 결과에서 보이는 첫번째 컬럼의 시간들이 위에서 보는 바와 다르다거나, 마지막 5개의 행의 이름의 순서가 다르다고 해도 놀랄 필요는 없다. 그 순서는 첫번째 컬럼에서 보여진 시간(times)에 기반한 것이다.

쓰레드 로컬 변수는 단순히 인스턴스 변수들의 또 다른 형태만이 아니다. 만약 특정 클래스의 10개의 인스턴스를 모두 일반적인 쓰래드내에서 접근한다면, 그것들의 ThreadLocal 정보는 모두 같을 것이다. 그러나, 만약 이러한 인스턴스들 중의 하나를 다른 쓰레드에서 접근하게 되면, ThreadLocal 정보는 바뀌게 될 것이다.

ThreadLocal 클래스는 오직 3개의 메소드만을 포함하고 있는데 get, setinitialValue가 바로 그것이다. ThreadLocal1 예제를 보면, set메소드는 인스턴스가 생성된 이후 호출되는 것을 알 수 있다. ThreadLocal의 상태를 이후에 다시 초기화하는 방법 대신에, ThreadLocal을 상속한 서브클래스에서 initialValue 메소드를 오버라이드할 수 있다. 다음의 ThreadLocal2 클래스가 바로 그 일을 하는데, 생성된 출력값은 ThreadLocal1의 경우와 비슷하다.

import java.util.Random;
public class ThreadLocalTest2 {
 // Create thread local class
 // Initial value is a random number from 0-999
 private static class MyThreadLocal extends ThreadLocal {
  Random random = new Random();
  protected Object initialValue() {
   return new Integer(random.nextInt(1000));
  }
 }
 // Define/create thread local variable
 static ThreadLocal threadLocal = new MyThreadLocal();
 // Create class variable
 static volatile int counter = 0;
 // For random number generation
 static Random random = new Random();
 // Displays thread local variable, counter,
 // and thread name
 private static void displayValues() {
  System.out.println(threadLocal.get() + "\t" + counter + "\t"
    + Thread.currentThread().getName());
 }
 public static void main(String args[]) {
  // Each thread increments counter
  // Displays variable info
  // And sleeps for the random amount of time
  // Before displaying info again
  Runnable runner = new Runnable() {
   public void run() {
    synchronized (ThreadLocalTest2.class) {
     counter++;
    }
    displayValues();
    try {
     Thread.currentThread().sleep(
       ((Integer) threadLocal.get()).intValue());
     displayValues();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  };
  // Another instance of class created
  // and values displayed
  displayValues();
  // Here's where the other threads
  // are actually created
  for (int i = 0; i < 5; i++) {
   Thread t = new Thread(runner);
   t.start();
  }
 }
}

ThreadLocalinitialValue 메소드는 각각의 쓰레드가 상태를 알아내기 위해 하는 첫번째 호출에서 사용된다. Set이 이미 호출된 상태라면, initialValue 메소드는 절대로 호출되지 않는다. initialValue 메소드를 사용할 때, public이 아니라 protected로 만들어야 한다는 것을 기억하자. 일반적으로 initialValue 메소드가 임의로 호출되는 것은 바람직하지 않기 때문이다. initialValue 가 오버라이드되지 않았을 경우, 쓰레드 로컬 변수를 나타내는 객체의 초기 값은 null이다.

ThreadLocal변수에 담을 컨텐츠는 어떤 Object 타입이든지 가능하다. ThreadLocal2에서 컨텐츠는 Integer이다. get을 이용해서 상태를 받아낼 때 Object를 적절한 타입으로 캐스팅해야 한다는 것을 기억하자.

InheritableThreadLocal라고 불리는 ThreadLocal의 두번째 타입을 보자. ThreadLocal과 같이, InheritableThreadLocal을 사용하는 각각의 쓰레드는 고유의 상태객체를 갖는다. ThreadLocalInheritableThreadLocal의 차이점은 InheritableThreadLocal의 초기값은 생성하는 쓰레드로부터 변수의 상태를 얻어낸다는 데에 있다. 상태의 변경은 다른 쓰레드를 제외한 로컬 쓰레드에서만 가능하다. InheritableThreadLocal의 새로운 childValue 메소드는 그것의 상태를 초기화하기위해 사용되는데 이는 상태를 얻어내려고 할 때가 아니라 자녀객체의 생성자에 의해 호출된다.

실제로 ThreadLocal의 사용을 검토하기 위해서는, java.nio.charsetCharset 클래스를 보거나 java.util.LoggingLogRecord를 참고한다. Charset의 경우에는, ThreadLocal가 캐릭터 세트 인코더와 디코더를 검색하기 위해 사용된다. 이것은 초기화하는 코드들이 절절한 시기에 사용되도록 조절하는 역할을 한다. LogRecord에서 ThreadLocal의 사용은 각각의 쓰레드에 Login을 위한 고유한 id가 할당되는 것을 보장해 준다.

Posted by '김용환'
,

자바 로그 관련

java core 2007. 1. 2. 23:25

 

자바로 로그에 대한 좋은 설명과 예제가 있었음

 

 

http://www.javapractices.com/Topic143.cjp

 

Home | web4j | Poll | Feedback | Source Code | Links
Logging messages
Topic :

Java's logging facility (see Sun's overview and API ) has two parts : a configuration file, and an API for using logging services. It has impressive flexibility. Log entries can be sent to these destinations, as either simple text or as XML :
  • the console
  • a file
  • a stream
  • memory
  • a TCP socket on a remote host
The Level class defines seven levels of logging enlightenment :
  • FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE
  • ALL and OFF are defined values as well
Here is one style of using these Levels in code, which may be modified as desired :
  • upon startup, use CONFIG to log configuration parameters
  • during normal operation, use INFO to log high-level "heartbeat" information
  • when bugs or critical conditions occur, use SEVERE
  • debugging information might default to FINE, with FINER and FINEST used occasionally, according to taste.
There is flexibility in how logging levels can be changed at runtime, without the need for a restart :
  • simply change the configuration file and call LogManager.readConfiguration.
  • or, change the level in the body of your code, using the logging API ; for example, one might automatically increase the logging level in response to unexpected events
Levels are attached to these items :
  • an originating logging request (from a single line of code)
  • a Logger (usually attached to the package containing the above line of code)
  • a Handler (attached to an application)
The flow of execution for a particular logging request usually proceeds as follows :

logging request of some level is made to logger attached to current package
if the request level is too low for that package's logger {
   discard it
}
otherwise {
   cycle through all handlers {
      if the request level is too low for that handler {
         discard it
      }
      otherwise {
         log the request
      }
   }
}

Here is an example of a logging configuration file :

# Properties file which configures the operation of the JDK
# logging facility.

# The system will look for this config file, first using
# a System property specified at startup:
#
# >java -Djava.util.logging.config.file=myLoggingConfigFilePath
#
# If this property is not specified, then the config file is
# retrieved from its default location at:
#
# JDK_HOME/jre/lib/logging.properties

# Global logging properties.
# ------------------------------------------
# The set of handlers to be loaded upon startup.
# Comma-separated list of class names.
# (? LogManager docs say no comma here, but JDK example has comma.)
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# Loggers and Handlers may override this level
.level=INFO

# Loggers
# ------------------------------------------
# Loggers are usually attached to packages.
# Here, the level for each package is specified.
# The global level is used by default, so levels
# specified here simply act as an override.
myapp.ui.level=ALL
myapp.business.level=CONFIG
myapp.data.level=SEVERE

# Handlers
# -----------------------------------------

# --- ConsoleHandler ---
# Override of global logging level
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# --- FileHandler ---
# Override of global logging level
java.util.logging.FileHandler.level=ALL

# Naming style for the output file:
# (The output file is placed in the directory
# defined by the "user.home" System property.)
java.util.logging.FileHandler.pattern=%h/java%u.log

# Limiting size of output file in bytes:
java.util.logging.FileHandler.limit=50000

# Number of output files to cycle through, by appending an
# integer to the base file name:
java.util.logging.FileHandler.count=1

# Style of output (Simple or XML):
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter

An application should likely centralize the naming policy for Loggers, since switching naming styles becomes a simple edit in one method, instead of a large number of edits spread throughout the application. Reasons to alter the naming policy might include :

  • in a shared environment, adding an application name to the Logger name can help distinguish one application's entries from others
  • adding application version information might be beneficial for debugging
Here is an example of using the logging API :
package myapp.business;

import java.util.logging.*;

/**
* Demonstrate Java's logging facilities, in conjunction
* with a logging config file.
*/
public final class SimpleLogger {

  public static void main(String argv[]) {
    SimpleLogger thing = new SimpleLogger();
    thing.doSomething();
  }

  public void doSomething() {
    //Log messages, one for each level
    //The actual logging output depends on the configured
    //level for this package. Calls to "inapplicable"
    //messages are inexpensive.
    fLogger.finest("this is finest");
    fLogger.finer("this is finer");
    fLogger.fine("this is fine");
    fLogger.config("this is config");
    fLogger.info("this is info");
    fLogger.warning("this is a warning");
    fLogger.severe("this is severe");

    //In the above style, the name of the class and
    //method which has generated a message is placed
    //in the output on a best-efforts basis only.
    //To ensure that this information is always
    //included, use the following "precise log"
    //style instead :
    fLogger.logp(Level.INFO, this.getClass().toString(), "doSomething", "blah");

    //For the very common task of logging exceptions, there is a
    //method which takes a Throwable :
    Throwable ex = new IllegalArgumentException("Some exception text");
    fLogger.log(Level.SEVERE, "Some message", ex);

    //There are convenience methods for exiting and
    //entering a method, which are at Level.FINER :
    fLogger.exiting(this.getClass().toString(), "doSomething");

    //Display user.home directory, if desired.
    //(This is the directory where the log files are generated.)
    //System.out.println("user.home dir: " + System.getProperty("user.home") );
  }

  // PRIVATE //

  //This logger will inherit the config of its parent, and add
  //any further config as an override. A simple style is to use
  //all config of the parent except, perhaps, for logging level.

  //This style uses a hard-coded literal and should likely be avoided:
  //private static final Logger fLogger = Logger.getLogger("myapp.business");

  //This style has no hard-coded literals, but forces the logger
  //to be non-static.
  //private final Logger fLogger=Logger.getLogger(this.getClass().getPackage().getName());

  //This style uses a static field, but hard-codes a class literal.
  //This is probably acceptable.
  private static final Logger fLogger =
                     Logger.getLogger(SimpleLogger.class.getPackage().getName());
} 


Example Run 1 , using the config file defined above :

>java -Djava.util.logging.config.file=C:\Temp\logging.properties
-cp . myapp.business.SimpleLogger

ConsoleHandler is configured to show only SEVERE messages :

Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
        at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
        at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

While the FileHandler shows ALL that is sent to it :

Jan 8, 2003 10:43:46 AM myapp.business.SimpleLogger doSomething
CONFIG: this is config
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:43:47 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
 at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
 at myapp.business.SimpleLogger.main(SimpleLogger.java:13)
 

Example Run 2, showing the ConsoleHandler output, using the logging.properties file which ships with the JDK, and which logs only INFO level and above :

>java -cp . myapp.business.SimpleLogger

Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:52:31 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
        at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
        at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

Would you use this technique?

Yes   No   Undecided   
Add your comment to this Topic :
Comment :
 
© 2006 John O'Hanley (Canada) | Source Code | Contact | Sponsor This Site | License | RSS
Over 53,000 unique IPs (and 106,000 sessions) last month.
- In Memoriam : Bill Dirani -

 

Posted by '김용환'
,

shift에 대해서.

java core 2006. 11. 16. 03:06


이번에 자바코딩을 하다가 제가 문득 잊고 사는 것을 하나 배웠지요.

 

하나는 바로 operator precedent (연산자 우선순위)구요.

저도 모르게 + 연산자가 우선순위가 높다라는 사실을 늘 잊어버린적이 많죠..  14개 연산자 등급중 4위라는 사실입니다.. ㅡ.ㅡ;

 

a & 0xFF00 >> 8 + 1* 2
=> 연산순서
(1)  1 * 2
(2)  8 + 1 * 2
(3) 0xFF00 >> 8 + 1* 2
(4) a & 0xFF00 >> 8 + 1 * 2

이렇게 됩니다.~

 


까먹지 않기 위해서 다시 공부합니다~

우선순위 연산자
1 . () []
2 ! ~ ++ --
3 * / %
4 + -
5 < < >> >>>
6 < &$60;= > >=
7 == !=
8 &
9  
10 ㅣ
11 &&
12 ㅣㅣ
13 ?:
14 = += -= * = /= %= &= = ㅣ= <<= >>= 


또 하나는 바로 쉬프트 연산입니다.
쉬프트 연산중 자바의 특성과 관련된 쉬프트가 있습니다. 즉, 최상위비트(Left Most bit )가 1일때 (즉, 음수) 쉬프트연산을 우측으로 할 지를 잘 결정해야 할 것 같더라구요.

즉,

 
shift는 총 3가지 종류가 있는데, left shift, right shift, zero fill right shfit가 있습니다.

-----------------------------


public class Shift {
    public static void main(String[] args) {
        int b, c;
        int a = 0xFF000000;
        b = a << 1;
        c = a >> 1;
       
        System.out.println("b:" + Integer.toHexString(b));
        System.out.println("c:" + Integer.toHexString(c));
    }
}

결과
b:fe000000
c:ff800000

------------------------------

 

public class Shift {
    public static void main(String[] args) {
        int b, c;
        int a = 0xFF000000;
        b = a << 1;
        c = a >>> 1;
       
        System.out.println("b:" + Integer.toHexString(b));
        System.out.println("c:" + Integer.toHexString(c));
    }
}

결과
b:fe000000
c:7f800000

 

c언어의 경우는 다음과 같이 테스트를 해볼 수 있지요.

int main()
{
    int b;
    int c;
    int a = 0xFF000000;
    b = a << 1;
    c = a >> 1;
   
    printf("b:%x\n", b);
    printf("c:%x\n", c);
}

b:fe000000
c:ff800000


근데 말이져..

c언어에서. 자바의 >>> 연산자가 따로 없는 듯 하네요... 저한테 c책이 없는데, 확인해 볼 길이 없네요.

 

즉, shift 연산을 할 때, arithmetic 값이 아닌, logical 값일때에 대한 shift는 c언어에서 따로 제공하는 것은 없지 않을까란 생각이 듭니다. 즉 c언어는 2의 보수표현의 arithmetic값에 치중되어 있지 않나 생각이 들어요.

예문을 보면, 원하는 것은 eF80000 값인데, 이 값으로 한번에 변환시켤 줄 수 있는 방법이 떠오르지 않네요. 늘, 코더가 저걸 생각하고, (a >> 1) & 0xeffffffff 이렇게 하는 수 밖에 없지 않나 생각드네요..

 

혹시 다른 방법 있으면 멀까요?

 

Posted by '김용환'
,

CRC-16/32

CRC(Cyclic Redundancy Check)는 시리얼 전송에서 데이타의 신뢰성을 검증하기 위한 에러 검출 방법의 일종이다.

간단한 에러 검출방법으로는 parity 비트에 의한 방법과 check-sum에 의한 에러 검출 방법이 있지만 parity 비트에 의한 방법은 데이타 중에 한꺼번에 2비트나 4비트가 변하게 되면 검출을 할 수 없고, check-sum에 의한 방법은 한 바이트에서 +1, 다른 바이트에서는 -1로 에러가 생기는 경우만 해도 에러는 검출 되지 않는다. 즉, 이들 방법으로는 에러를 검출해 낼 수 있는 확률이 대단히 낮다.

 

CRC에 의한 방법은 높은 신뢰도를 확보하며 에러 검출을 위한 오버헤드가 적고, 랜덤 에러나 버스트 에러를 포함한 에러 검출에 매우 좋은 성능을 갖는 것을 특징으로 한다.

이러한 CRC 방법으로 보통 2가지 종류가 사용 되는데, 원칩 마이크로 프로세서와 같이 간단한 용도에서는 CRC-16 이 사용되고, 이보다 더욱 정확한 에러 검출이 필요한 경우에는 CRC-32를 사용한다.

ZIP, ARJ, RAR 과 같은 압축 프로그램이나 플로피 디스크 등의 데이터 검증 용도에 널리 사용되고 있다.

 

기본 원리

n 비트의 주어진 정보가 있을 때, 이를 k 비트 만큼 자리를 올리고 미리 약속한 k 비트의 키 값으로 나누면 r 비트의 나머지가 남게 된다. 송신측에서는 원래의 정보 비트를 k 비트 자리 올린 것에 r 비트의 나머지를 더해서 n+r 비트의 데이타를 만들어 보낸다.
수신측에서는 수신된 n+r 비트의 데이타를 키 값으로 나누어 보고 나머지가 정확히 0 이 되는지를 검사하면 된다.

이 때 k 가 16 비트이면 CRC-16, 32 비트이면 CRC-32 가 되고 키 값으로는 수학자 들에 의해 정해진 값을 주로 사용한다.
CRC-16 에는 0x8005, CRC-32 에는 0x04c11db7 이 많이 사용된다. 그리고 r 비트의 나머지를 Frame Check Sequence(FCS)라고 부른다.

 

여기서 CRC 계산에 사용되는 modulo-2 연산의 세계를 살펴보자.

CRC 계산시의 사칙연산은 carry를 고려하지 않는다.
1 + 1 = 0 (carry는 생각하지 않음)
0 - 1 = 1
덧셈 연산은 뺄셈 연산과 결과가 같으며 XOR 연산과도 같다.

 

다항식 표현 방법을 통해 CRC 계산 방법을 살펴보자.

(1) 2진 다항식으로 표시

예) 비트열 101 --> 다항식 x2 + 1

정보 다항식: 데이터 비트열의 다항식으로 P(x) = pn xn-1 + ... + p3x2 + p2x1 + p1
CRC 다항식: CRC 생성을 위한 다항식으로 G(x) (미리 정해진 키 값)
몫: Q(x)
나머지: R(x)
전송 데이타: T(x)

 

(2) CRC 계산 방법

P(x)를 k 비트 만큼 자리를 올리고( P(x)에 xk를 곱하는 것과 동일) G(x)로 나누면

xk P(x) = Q(x)*G(x) +/- R(x) 이다.

(k는 CRC 다항식의 최고 차수)

R(x) = dk xk-1 + .. + d2x1 + d1 ( R(x)의 최고 차수는 k-1)

비트열 dk ... d2 d1 을 FCS(Frame Check Sequence) 라 함

위 식에서 xk P(x) + R(x) 는 Q(x)*G(x) 와 같다.

즉, xk P(x) + R(x) 는 G(x)의 배수이므로 G(x) 로 나누면 나머지가 0 임을 알 수 있다.

결과적으로 전송되는 데이터 비트열 : pn ... p3 p2 p1 dk ... d2 d1

즉, 전송 T(x) = xk P(x) + R(x)

 

예) 데이터 비트열 110011 즉 P(x) =x5+x4+x1+x0, CRC 다항식 G(x) = x4+x3+1, 즉 11001일 경우 FCS와 전송되는 비트열은?

xkP(x) = x4 (x5 + x4 + x1 + 1) = x9 + x8 + x5 + x4, 비트열로 표시하면 1100110000

 

                 100001
         _____________
11001 | 1100110000
           11001
          ____________
                   10000
                   11001
          ____________
                     1001    

 

xkP(x) = Q(x)G(x) - R(x)에서

Q(x) = x5 + x0 이므로,

R(x) = x3 + x0, ---> FCS는1001

따라서 전송되는 비트열 1100111001

 

연산의 최적화

CRC의 계산은 일반 나눗셈 명령을 이용해 구현할 수 없다. 1비씩 shift 하면서 XOR 연산을 통해 나머지를 구해야 한다.
하지만 정보 비트에 대해 하나하나씩 연산을 하는 것에는 분명 속도 개선의 여지가 있다.
실제 계산 시에는 모든 바이트에 대해 CRC 다항식에 대한 CRC 값을 계산해 표로 만들어 두고 들어오는 데이타를 인덱스로 삼아 계산 값을 바로 얻는 방법을 사용한다.

 

CRC-16 C소스 : crc16h.c
CRC-32 C소스 : crc32h.c

8051 어셈블리 CRC-8 소스 : 8051crc8.zip
8051 어셈블리 CRC-16 소스 : 8051crc16.zip

 

Posted by '김용환'
,

CRC(cyclic redundancy checking) : 주기적 덧(붙임) 검사

CRC는 통신 링크로 전송되어온 데이터 내에 에러가 있는지 확인하기 위한 방법 중의 하나이다. 송신장치는 전송될 데이터 블록에 16 비트 또는 32 비트 다항식을 적용하여, 그 결과로 얻어진 코드를 그 블록에 덧붙인다. 수신측에서는 데이터에 같은 다항식을 적용하여 그 결과를 송신측이 보내온 결과와 비교한다. 만약 두 개가 일치하면, 그 데이터는 성공적으로 수신된 것이며, 그렇지 않은 경우 그 데이터 블록을 재 송신하도록 송신측에게 요구한다.

 

ITU-T(이전의 CCITT)는 송신블록에 부가될 코드를 얻는데 사용되는 16 비트 다항식에 대한 표준을 제정했다. IBM의 SDLC와 다른 프로토콜들은 CRC-16과 다른 16 비트 다항식을 사용한다. 16 비트 CRC는 두 개의 비트가 동시에 에러가 난 경우를 포함하여, 일어날 수 있는 모든 에러에 대하여 99.998% 검출을 보장한다. 이 정도의 검출보증은 4 KB 이하의 데이터 블록 전송에는 충분한 것으로 평가되고 있으며, 그 이상의 대량 전송에는 32 비트 CRC가 사용된다. 이더넷과 토큰링 프로토콜에서도 모두 32 비트 CRC를 사용한다.

 

다소 덜 복잡하고 에러 검출능력도 다소 떨어지는 방법으로서 체크섬 방식이 있다. 이러한 방법을 사용하는 프로토콜 목록을 보려면 모뎀 에러교정 프로토콜을 참조하라


 

16비트 CRC 값 계산 코드

디바이스와 데이타를 교환하는 프로그램을 작성할 때 crc 체크가 제대로 되는지 확인하기 위하여 테스트용으로 16비트 crc 값을 생성하는 짧은 코드를 만들어 보았다. 사용 방법은 crc값을 만들고자 하는 데이타를 파일로 저장하고 아래 함수의 인자로 파일명을 전달해 주면 된다.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
 
unsigned int CRC16R(char data, unsigned int crc16);
 
void main(int argc, char *argv[])
{
        FILE *in;       
        //unsigned char data[250];
        unsigned char *data;
        long len,fl,i;
        int tc;
        unsigned char temp;
        
        unsigned int crc1,crc2,crc16;
        
        crc1=0;
        crc2=0;
        crc16=0xffff;
 
        if(argc<2){
                printf("usage : crc <filename>\n");
                return;
        }
 
        if((in = fopen(argv[1],"rb")) == NULL){
                printf("can't open %s\n",argv[1]);
                return;
        }
 
        // read from file
        fseek(in,0L,SEEK_END);
        fl = ftell(in);
        fseek(in,0L,SEEK_SET);
 
        data = (unsigned char *)malloc(fl);
        memset(data, '\0', sizeof(data));
 
        for(len=0;len<fl;len++) {
                tc = fgetc(in);
                if (EOF==tc) {
                        fclose(in);
                }
                data[len]= (unsigned char)tc;
        }
        
        data[len]='\0';
 
        // calc. crc
        for     (i=0;i<len;i++){
                temp = data[i];
                crc16=CRC16R(temp, crc16);
                printf("0x%2x, 0x%4x\n", temp, crc16);
        }
 
        crc1 = crc16 & 0xff;
        if (crc1 <0x80) crc1 ^= 0xff;
        crc2 = ((crc16>>8)&0xff);
        if (crc2 <0x80) crc2 ^= 0xff;
 
        printf("crc16 is 0x%4x\n",crc16);
        printf("crc1 is 0x%2x\n",crc1);
        printf("crc2 is 0x%2x\n",crc2);
 
        free(data);
        getch();
}
 
unsigned int CRC16R(unsigned char data, unsigned int crc16)
{
        int i;
        unsigned int temp_crc16;
        unsigned char temp_data;
 
        temp_crc16 = crc16;
        temp_data = (unsigned char) data;
 
        temp_data <<=1;
        for ( i = 8; i > 0; i--){
 
                temp_data >>= 1;
                if ((temp_data ^ temp_crc16) & 0x0001)
                        temp_crc16 = (temp_crc16 >> 1) ^ 0x8408;
                else
                        temp_crc16 >>= 1;
        }
        return(temp_crc16);
}

 

출처: http://home.dasomnetwork.com/~leedw

Posted by '김용환'
,

Checksum의 이용
컴퓨터 소프트웨어 분야에서의 "checksum"은 바이트 스트림에서 계산된 하나의 값입니다. checksum은 바이트의 signature, 다시 말해서 연산을 이용한 바이트들의 결합이라고 할 수 있습니다. 중요한 것은, 바이트 스트림에서의 변화와 변조가 높은 확률로 간파될 수 있다는 것입니다.

checksum의 예는 데이터 전송에서 찾을 수 있습니다. 어떤 응용프로그램이 100바이트의 정보를 네트웍을 통해 다른 응용프로그램으로 전달할 때, 그 응용 프로그램은 바이트의 값에서 계산된 32 비트 checksum을 추가합니다. 이 정보를 받는 쪽에서 이 checksum은 받은 100 바이트를 기초로해서 다시 계산됩니다. 수신하는 쪽의 checksum이 발신 쪽에서 보내온 것과 다르면, 데이터가 어떠한 방법으로 변조되었음을 알려줍니다.

checksum은 (전형적으로) 계산한 데이터보다도 훨씬 더 작습니다. 그렇기 때문에 대부분의 에러를 가려 내기 위해서는 확률적인 모델에 의지합니다. 바이트 순서에서 숫자를 계산하기 위해서 연산법칙이 적용되었다는 점에서 checksum은 해쉬 코드와 아주 유사하다고 할 수 있습니다.

java.util.zip.CRC32 클래스는 checksum 연산의 표준의 하나인 CRC-32를 구현하고 있습니다. checksum의 사용 방법에 대해서는 다음 응용프로그램을 보십시오.
텍스트 파일에 몇 가지 스트링을 입력하고 있으며, 이 문자열 리스트가 입력 후 변경되었는지 알고 싶은 경우, 예를 들자면, 누가 파일을 편집하기 위해서 텍스트 에디터를 사용했는지 알아 보고 싶은 경우를 생각해봅시다. 응용프로그램을 포함하는 프로그램이 여기에 두가지 있습니다. 처음 프로그램은 파일에 문자열 세트를 입력하고, 문자열에서 checksum을 계산합니다.

    import java.io.*;
    import java.util.zip.CRC32;

    public class Checksum1
    {
        // 파일에 기록할 이름들의 목록
        static final String namelist[] =
        {
            "Jane Jones",
            "Tom Garcia",
            "Sally Smith",
            "Richard Robinson",
            "Jennifer Williams"
        };

        public static void main(String args[])
                           throws IOException
        {
            FileWriter fw = new FileWriter("out.txt");
            BufferedWriter bw = new BufferedWriter(fw);
            CRC32 checksum = new CRC32();

            // 목록의 길이를 입력
            bw.write(Integer.toString(namelist.length));
            bw.newLine();

            // 각 이름을 기록하고 checksum 업데이트
            for (int i= 0; i < namelist.length; i++)
            {
                String name = namelist[i];
                bw.write(name);
                bw.newLine();
                checksum.update(name.getBytes());
            }

            // checksum 기록
            bw.write(Long.toString(checksum.getValue()));
            bw.newLine();

            bw.close();
        }
    }

이 프로그램의 출력은 "out.txt" 파일에 다음과 같은 내용물로 되어 있습니다;

    5
    Jane Jones
    Tom Garcia
    Sally Smith
    Richard Robinson
    Jennifer Williams
    4113203990

마지막 줄의 숫자는 스트링 문자에서 찾아낸 모든 바이트를 결합한 것을 계산한 checksum입니다.
두번째 프로그램은 파일을 읽습니다;

    import java.io.*;
    import java.util.zip.CRC32;

    public class Checksum2
    {
        public static void main(String args[])
                           throws IOException
        {
            FileReader fr = new FileReader("out.txt");
            BufferedReader br = new BufferedReader(fr);
            CRC32 checksum = new CRC32();

            // 파일에서 이름의 수를 읽어낸다
            int len = Integer.parseInt(br.readLine());

            // 파일에서 각 이름을 읽어내고 checksum을 업데이트
            String namelist[] = new String[len];
            for (int i = 0; i < len; i++)
            {
                namelist[i] = br.readLine();
                checksum.update(namelist[i].getBytes());
            }

            // checksum을 읽어낸다
            long cs = Long.parseLong(br.readLine());

            br.close();

            // checksum이 일치하지 않으면 에러를 출력하고, 그렇지 않으면 이름의 목록을 출력
            if (cs != checksum.getValue())
            {
                System.err.println("*** bad checksum ***");
            }
            else
            {
                for (int i = 0; i < len; i++)
                {
                    System.out.println(namelist[i]);
                }
            }
        }
    }

이 프로그램은 파일에서 이름의 명단을 읽어내고 이름을 출력합니다. "out.txt"를 텍스트 편집기로 편집하여 이름 중 하나를 바꾸면, 예를 들어서 "Tom"을 "Thomas"로 바꾼다면, 이 프로그램은 다른 checksum을 계산할 것이고, checksum 에러 메세지를 낼 것입니다.

이제, 누구든지 고의로 텍스트 파일을 변조 시키고, 새로운 checksum을 계산하고 그것 역시 변조시킬 수도 있다고 생각할 수도 있습니다. 이것은 사실상 가능한 일이긴 하나, 결코 쉬운 일은 아닙니다. 그것은 CRC-32 checksum 연산이 일반 사용자에게 그다지 쉬운 것이 아니고, 새로운 checksum 값이 무엇이어야하는지 계산하는 것이 어렵기 때문입니다.

checksum을 사용하는 또 다른 방법은 java.util.zip 에 있는 CheckedInputStream과 CheckedOutputStream 클래스를 사용하는 방법입니다. 이 클래스들은 입출력 스트림에서 실행되는 checksum 의 계산을 지원합니다.
 

출처 : http://www.javastudy.co.kr/docs/techtips/000411.html

Posted by '김용환'
,
Posted by '김용환'
,

See The JavaTM Virtual Machine Specification, 4.1 ClassFile.

================
A class file contains a single ClassFile structure:

    ClassFile {
        (...cut...)
        u2 minor_version;
        u2 major_version;
        (...cut...)
    }

The items in the ClassFile structure are as follows:

(...cut...)
       minor_version, major_version

       The values of the minor_version and major_version items are the minor and major version numbers of the compiler that produced this class file. An
       implementation of the Java Virtual Machine normally supports class files having a given major version number and minor version numbers 0 through
       some particular minor_version.

       If an implementation of the Java Virtual Machine supports some range of minor version numbers and a class file of the same major version but a higher
       minor version is encountered, the Java Virtual Machine must not attempt to run the newer code. However, unless the major version number differs, it will
       be feasible to implement a new Java Virtual Machine that can run code of minor versions up to and including that of the newer code.

       A Java Virtual Machine must not attempt to run code with a different major version. A change of the major version number indicates a major
       incompatible change, one that requires a fundamentally different Java Virtual Machine.

       In Sun's Java Developer's Kit (JDK) 1.0.2 release, documented by this book, the value of major_version is 45. The value of minor_version is 3. Only
       Sun may define the meaning of new class file version numbers.

 

 

http://www.kaffe.org/pipermail/kaffe/1999-March/088319.html

Posted by '김용환'
,