List.subList(from, to) 메소드가 당연히 from <= list <=to 인줄 알았는데,

알고보니. from <= list < to 였다. toIndex 자리의 



api에 보니. toIndex 는 exclusive 라.... 그 위치는 제외된다. 


예제)

public class Test {

public static void main(String[] args) {

List<String> list = new ArrayList<String>();

list.add("a");

list.add("b");

list.add("c");

list.add("d");

System.out.println(list.subList(0, 2));

}

}

  

나의 예상은 abc 였건만.. 결과는 2개였다. 


결과

[a, b]



<API>

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#substring(int,%20int)


substring

public String substring(int beginIndex,
               int endIndex)
Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1. Thus the length of the substring is endIndex-beginIndex.

Examples:

 "hamburger".substring(4, 8) returns "urge"
 "smiles".substring(1, 5) returns "mile"
 
Parameters:
beginIndex - the beginning index, inclusive.
endIndex - the ending index, exclusive.


Posted by '김용환'
,



이클립스 상에서 다음 코드에서 에러가 난다. java 7 / sts 3.4.0 release

import sun.misc.BASE64Decoder;

import sun.misc.BASE64Encoder;



이유는 이클립스 설정에서 나오는 것이었다. 


Windows -> Preference -> Java -> Compiler -> Errors/Warnings

에서 Deprecated and restricted API의 Forbidden reference(access rules)의 단계를 error가 아닌 warning이나 ignore로 바꾸면 된다. 



Posted by '김용환'
,


1. 서론 

http://bugs.sun.com/view_bug.do?bug_id=6427854

http://bugs.sun.com/view_bug.do?bug_id=6527572



jdk5, jdk6에서 Chartset.forName() 또는 Selector.open() 호출시 random(rarely)하게 NullPointerException(NPE)가 발생되는 문제가 있었다. 이 이유는 Selector.open() 호출시 thread safe하지 않으면서 생긴 문제이다. 



java.lang.NullPointerException
   at sun.nio.ch.Util.atBugLevel(Util.java:290)
   at sun.nio.ch.SelectorImpl.<init>(SelectorImpl.java:40)
   at sun.nio.ch.WindowsSelectorImpl.<init>(WindowsSelectorImpl.java:104)
   at sun.nio.ch.WindowsSelectorProvider.openSelector(WindowsSelectorProvider.java:26)
   at java.nio.channels.Selector.open(Selector.java:209)



오픈소스쪽은 어떻게 구현되어 있는지 살펴보고, 앞으로 만들 솔루션들을 미리 대비해본다.



2. Zookeeper 사례

이런 문제를 해결하기 위해서 Zookeeper에서는 다음과 같이 해결했다. 이미 열고 닫음으로서 미리 초기화를 시켜 thread safe로 인한 문제가 없도록 했다. tokyotyrant도 비슷하게 사용.



<NIOServerCnxnFactory.java>


    static {

      .... 

        /**

         * this is to avoid the jvm bug:

         * NullPointerException in Selector.open()

         * http://bugs.sun.com/view_bug.do?bug_id=6427854

         */

        try {

            Selector.open().close();

        } catch(IOException ie) {

            LOG.error("Selector failed to open", ie);

        }

    }




3. Tomcat 사례

Tomcat은 synchronized(Selector.class)를 사용해서 thread safe하도록 했다. 


<NioSelectorPool.java >
protected Selector getSharedSelector() throws IOException {
        if (SHARED && SHARED_SELECTOR == null) {
            synchronized ( NioSelectorPool.class ) {
                if ( SHARED_SELECTOR == null )  {
                    synchronized (Selector.class) {
                        // Selector.open() isn't thread safe
                        // http://bugs.sun.com/view_bug.do?bug_id=6427854
                        // Affects 1.6.0_29, fixed in 1.7.0_01
                        SHARED_SELECTOR = Selector.open();
                    }
                    log.info("Using a shared selector for servlet write/read");
                }
            }
        }
        return  SHARED_SELECTOR;
    }

 @SuppressWarnings("resource") // s is closed in put()
    public Selector get() throws IOException{
        if ( SHARED ) {
            return getSharedSelector();
        }
        if ( (!enabled) || active.incrementAndGet() >= maxSelectors ) {
            if ( enabled ) active.decrementAndGet();
            return null;
        }
        Selector s = null;
        try {
            s = selectors.size()>0?selectors.poll():null;
            if (s == null) {
                synchronized (Selector.class) {
                    // Selector.open() isn't thread safe
                    // http://bugs.sun.com/view_bug.do?bug_id=6427854
                    // Affects 1.6.0_29, fixed in 1.7.0_01
                    s = Selector.open();
                }
            }
            else spare.decrementAndGet();

        }catch (NoSuchElementException x ) {
            try {
                synchronized (Selector.class) {
                    // Selector.open() isn't thread safe
                    // http://bugs.sun.com/view_bug.do?bug_id=6427854
                    // Affects 1.6.0_29, fixed in 1.7.0_01
                    s = Selector.open();
                }
            } catch (IOException iox) {
            }
        } finally {
            if ( s == null ) active.decrementAndGet();//we were unable to find a selector
        }
        return s;
    }



3, terracotta

dso-common이란 패키지안에서 Selector.open() 에서 발생하는 NPE를 catch해서 계속 retry하도록 코딩되어 있다. 



CoreNIOServices.java

private Selector createSelector() {
    Selector selector1 = null;

    final int tries = 3;

    for (int i = 0; i < tries; i++) {
      try {
        selector1 = Selector.open();
        return selector1;
      } catch (IOException ioe) {
        throw new RuntimeException(ioe);
      } catch (NullPointerException npe) {
        if (i < tries && NIOWorkarounds.selectorOpenRace(npe)) {
          System.err.println("Attempting to work around sun bug 6427854 (attempt " + (i + 1) + " of " + tries + ")");
          try {
            Thread.sleep(new Random().nextInt(20) + 5);
          } catch (InterruptedException ie) {
            //
          }
          continue;
        }
        throw npe;
      }
    }

    return selector1;
  }



4. Netty 및 JBoss 및 기타 오픈소스.

실행 스크립트에  -Dsun.nio.ch.bugLevel="" 를 추가해서 NPE가 안나게 했다.
Netty의 경우는 소스 레벨에서 이런 작업을 추가했다.

<SelectorUtil.java>

   static {
46          String key = "sun.nio.ch.bugLevel";
47          try {
48              String buglevel = System.getProperty(key);
49              if (buglevel == null) {
50                  System.setProperty(key, "");
51              }
52          } catch (SecurityException e) {
53              if (logger.isDebugEnabled()) {
54                  logger.debug("Unable to get/set System Property '" + key + '\'', e);
55              }
56          }
57          if (logger.isDebugEnabled()) {
58              logger.debug("Using select timeout of " + SELECT_TIMEOUT);
59              logger.debug("Epoll-bug workaround enabled = " + EPOLL_BUG_WORKAROUND);
60          }
61      }



Posted by '김용환'
,


GC 로그 로테이션을 이용하려면 다음과 같이 사용하면 된다. 



-verbose:gc

-XX:+PrintGCDateStamps

-XX:+PrintGCDetails

-Xloggc:$LOG_DIR/gc.log

-XX:+UseGCLogFileRotation

-XX:NumberOfGCLogFiles=9

-XX:GCLogFileSize=20m




Posted by '김용환'
,


오라클 java 문서(http://www.oracle.com/technetwork/java/ergo5-140223.html) 에 따르면,


jvm을 server class로 실행하면 초기 heap size는 메모리의 1/64 이고, 최대 heap size는 1/4까지 늘어난다고 적혀 있다. 


  • initial heap size of 1/64 of physical memory up to 1Gbyte

  • maximum heap size of ¼ of physical memory up to 1Gbyte


그렇다면 리눅스에서 java 디폴트 머신의 메모리 한계를 알려면 다음의 명령어를 이용하면 된다. 


$ java -XX:+PrintFlagsFinal -version 2>&1 | grep -i -E 'heapsize|permsize|version'

    uintx AdaptivePermSizeWeight                    = 20              {product}           

    uintx ErgoHeapSizeLimit                         = 0               {product}           

    uintx InitialHeapSize                          := 62800640        {product}           

    uintx LargePageHeapSizeThreshold                = 134217728       {product}           

    uintx MaxHeapSize                              := 1006632960      {product}           

    uintx MaxPermSize                               = 85983232        {pd product}        

    uintx PermSize                                  = 21757952        {pd product}        



전체 메모리가 4G인 리눅스 머신에서 initial heap size 는 60 m, max heap size는 1G정도 나온다.

perm size는 20M이고, max perm size 는 82M가 된다.



일반적인 오픈 소스는 java memory설정을 디폴트로 하는 경우가 많으나, 운영을 필요로 하는 오픈소스의 경우는 java 메모리 설정을 점차적으로 많이 지정하는 분위기로 가는 듯 하다.

 아마도  사용자들이 디폴트로 사용하면서 이슈가 생기니 그러는 듯 하다. (디폴트는 오히려 좋은 것일수도 있다.)



hbase의 경우 conf/hbase-env.sh , bin/hbase 파일에 두어 주석을 달았지만 명시적으로 쓸 수 있도록 권고 하고 있다. hbase의 경우는 디폴트로 1G를 default메모리로 할당하고 있다. 



conf/hbase-env.sh 파일

# The maximum amount of heap to use, in MB. Default is 1000.

# export HBASE_HEAPSIZE=1000


conf/hbase 파일
JAVA_HEAP_MAX=-Xmx1000m

# check envvars which might override default args
if [ "$HBASE_HEAPSIZE" != "" ]; then
  #echo "run with heapsize $HBASE_HEAPSIZE"
  JAVA_HEAP_MAX="-Xmx""$HBASE_HEAPSIZE""m"
  #echo $JAVA_HEAP_MAX
fi


Zookeeper에 최근에 client와 server class에 따라서 java 디폴트 메모리 설정을 바꾸려는 시도가 진행중이다.  

3.4.6에 포함될 예정이다..


https://issues.apache.org/jira/browse/ZOOKEEPER-1670

https://issues.apache.org/jira/secure/attachment/12577655/ZOOKEEPER-1670.patch


+
+# default heap for zookeeper server
+ZK_SERVER_HEAP="${ZK_SERVER_HEAP:-1000}"
+export SERVER_JVMFLAGS="-Xmx${ZK_SERVER_HEAP}m $SERVER_JVMFLAGS"
+
+# default heap for zookeeper client
+ZK_CLIENT_HEAP="${ZK_CLIENT_HEAP:-256}" 

+export CLIENT_JVMFLAGS="-Xmx${ZK_CLIENT_HEAP}m $CLIENT_JVMFLAGS"






* 참고

http://stackoverflow.com/questions/3428251/is-there-a-default-xmx-setting-for-java-1-5

http://www.oracle.com/technetwork/java/ergo5-140223.html

https://issues.apache.org/jira/browse/ZOOKEEPER-1670


Posted by '김용환'
,



오픈소스 코드 보다 특이한 구글 패키지가 있어 확인을 하다 발견한 것!!


개발하다보면 자바 콜렉션만으로 개발을 못해서 직집 콜렉션을 만들고 그랬는데.

유용한 콜렉션이 많다. 


http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/package-summary.html



이게 google guava로 합쳐졌다.


[2012 자바카페 OPEN 세미나] Introduction to google guava from 흥래 김
Posted by '김용환'
,

 

자바 서버개발자보다는 자바 임베디드 개발자 내부 캐쉬 용도 용도로 쓰기 위해서 java.lang.ref.Reference클래스를 사용하기도 한다. 잘만 쓰면 메모릭 릭을 잘 방지하면서 사용할 수 있다.

 

* SoftReference(SR)

풍부하게 메모리를 넉넉히 쓰지만, 최초의 GC가 돌아간 후, JVM은 메모리 공간이 없으면 SoftRefernece 인스턴스를 gc 대상으로 잡는다. OOME 가 발생할 수 있는 상황에서 softly-reachable objects가 있다면, 이 objects를 모두 메모리에서 정리한다. 언제 gc될지는 모른다.

안드로이드 API(http://developer.android.com)에 따르면, as late as possible이다.

 

실제 구현 예)  Tomcat의 jdbc 쪽 SoftReferenceObjectPool.java을 참조하면 좋음

간단하게 new SoftReference(객체인스턴스)로 해서 만든다.

http://www.docjar.com/html/api/org/apache/commons/pool/impl/SoftReferenceObjectPool.java.html

package org.apache.commons.pool.impl;

import java.lang.ref.SoftReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.apache.commons.pool.BaseObjectPool;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.PoolUtils;


public class SoftReferenceObjectPool extends BaseObjectPool implements ObjectPool {
….



public synchronized void addObject() throws Exception {
        assertOpen();
        if (_factory == null) {
            throw new IllegalStateException("Cannot add objects without a factory.");
        }
        Object obj = _factory.makeObject();

        boolean success = true;
        if(!_factory.validateObject(obj)) {
            success = false;
        } else {
            _factory.passivateObject(obj);
        }

        boolean shouldDestroy = !success;
        if(success) {
           _pool.add(new SoftReference(obj, refQueue));
            notifyAll(); // _numActive has changed
        }

        if(shouldDestroy) {
            try {
                _factory.destroyObject(obj);
            } catch(Exception e) {
                // ignored
            }
        }
    }



* WeakReference(WR) , WeakHashMap

객체가 사용 중이기는 하지만, 더 이상 사용하지 않을 것 같으면 컨테이너에서 삭제한다.

쉽게 말해서 일종의 잠깐 동안의 캐쉬로서 gc가 실행되면 메모리에서 정리한다.

아래 코드를 실행하면, 설명은 그다지 필요하지 않다.


import java.lang.ref.WeakReference;

public class ReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        Student s1 = new Student(1);
        System.out.println("1: " + s1);
        WeakReference<Student> ws = new WeakReference<Student>(s1);
        System.out.println("2: " + ws.get());
        s1 = null;
        System.gc();
        Thread.sleep(1000);
        System.out.println("3: " + ws.get());
    }
}

class Student {
    int id;
    public Student(int id) {
        this.id = id;
    }
    public String toString() {
        return "[id=" + id + "]";
    }
}

결과는 다음과 같다. gc이후에 WeakReference 객체는 모두 메모리에서 정리된다. 

1: [id=1]
2: [id=1]
3: null

 

톰캣 컨테이너에서 사용하고 있는 ConcurrentCache.java를 예로 든다.

package org.apache.el.util;

import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

public final class ConcurrentCache<K,V> {

    private final int size;

    private final Map<K,V> eden;

    private final Map<K,V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<K,V>(size);
        this.longterm = new WeakHashMap<K,V>(size);
    }

    public V get(K k) {
        V v = this.eden.get(k);
        if (v == null) {
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            if (v != null) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k, v);
    }
}

java의 ThreadLocal.ThreadLocalMap에서도 WeakReference를 사용하고 있다.

static class ThreadLocalMap {

 
       static class Entry extends WeakReference<ThreadLocal> {
           /** The value associated with this ThreadLocal. */
           Object value;

           Entry(ThreadLocal k, Object v) {
               super(k);
               value = v;
           }
       }

       private static final int INITIAL_CAPACITY = 16;

 
       private Entry[] table;

    
       private int size = 0;

    
       private int threshold; // Default to 0


       private void setThreshold(int len) {
           threshold = len * 2 / 3;
       }

     
       private static int nextIndex(int i, int len) {
           return ((i + 1 < len) ? i + 1 : 0);
       }

 
       private static int prevIndex(int i, int len) {
           return ((i - 1 >= 0) ? i - 1 : len - 1);
       }

     
       ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
           table = new Entry[INITIAL_CAPACITY];
           int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
           table[i] = new Entry(firstKey, firstValue);
           size = 1;
           setThreshold(INITIAL_CAPACITY);
       }

   
       private ThreadLocalMap(ThreadLocalMap parentMap) {
           Entry[] parentTable = parentMap.table;
           int len = parentTable.length;
           setThreshold(len);
           table = new Entry[len];

           for (int j = 0; j < len; j++) {
               Entry e = parentTable[j];
               if (e != null) {
                   ThreadLocal key = e.get();
                   if (key != null) {
                       Object value = key.childValue(e.value);
                       Entry c = new Entry(key, value);
                       int h = key.threadLocalHashCode & (len - 1);
                       while (table[h] != null)
                           h = nextIndex(h, len);
                       table[h] = c;
                       size++;
                   }
               }
           }
       }

   …..

 

안드로이드 API(http://developer.android.com)에서는 구현체에 대해서 더욱 세밀하게 설명이 들어가 있다.

Implements a weak reference, which is the middle of the three types of references. Once the garbage collector decides that an object obj is is weakly-reachable, the following happens:

  • A set ref of references is determined. ref contains the following elements:
    • All weak references pointing to obj.
    • All weak references pointing to objects from which obj is either strongly or softly reachable.
  • All references in ref are atomically cleared.
  • All objects formerly being referenced by ref become eligible for finalization.
  • At some future point, all references in ref will be enqueued with their corresponding reference queues, if any.

 

 

* PhantomReference(PR)

soft, weak보다 엄청 약하다. gc가 돌기 전(즉, 이것은 gc 대상이야 라고 결정할 때, finalize() 호출 후 ) 메모리에서 정리된다. 아무래도 gc 되기 전에 cleanup 해야 할 때 사용한다. 내부적으로는 유지하고 있지만, 객체를 다시 꺼내오면 null이 된다.

아주 특수한 경우에 쓰인다. sun.misc.Cleaner  클래스가 PhantomReference를 사용하고 있다.

http://knight76.tistory.com/1548 

재미있는 블로그(http://weblogs.java.net/blog/kcpeppe/archive/2011/09/29/mysterious-phantom-reference)가 있는데, PhantomReference 썼다가 이상하게 (미스테리하게) 나와서, 조금 수정해서 해결했다는 내용이다.

package snippet;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;

public class ReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        test1();
        test2();
    }
   
    public static void test1() {
        ReferenceQueue<Foo> queue = new ReferenceQueue<Foo>();
        ArrayList<PhantomReference<Foo>> list = new ArrayList<PhantomReference<Foo>>();

        for (int i = 0; i < 10; i++) {
            Foo o = new Foo(Integer.toOctalString(i));
            list.add(new PhantomReference<Foo>(o, queue));
        }

        // make sure the garbage collector does it’s magic
        System.gc();

        // lets see what we’ve got
        Reference<? extends Foo> referenceFromQueue;

        for (PhantomReference<Foo> reference : list) {
            System.out.println("x: " + reference.isEnqueued());
        }
        while ((referenceFromQueue = queue.poll()) != null) {
            System.out.println("y: " + referenceFromQueue.get());
            referenceFromQueue.clear();
        }
    }

    public static void test2() {
        // initialize
        ReferenceQueue<Foo> queue = new ReferenceQueue<Foo>();
        ArrayList< FinalizeStuff<Foo>> list = new ArrayList<FinalizeStuff<Foo>>();
        ArrayList<Foo> foobar = new ArrayList<Foo>();
        
        for ( int i = 0; i < 10; i++) {
            Foo o = new Foo( Integer.toOctalString( i));
            foobar.add(o);
            list.add(new FinalizeStuff<Foo>(o, queue));
        }
        
        // release all references to Foo and make sure the garbage collector does it’s magic
        foobar = null;
        System.gc();
        
        // should be enqueued
        Reference<? extends Foo> referenceFromQueue;
        for ( PhantomReference<Foo> reference : list) {
            System.out.println(reference.isEnqueued());
        }
        
        // now we can call bar to do what ever it is we need done
        while ( (referenceFromQueue = queue.poll()) != null) {
            ((FinalizeStuff)referenceFromQueue).bar();
            referenceFromQueue.clear();
        }
    }
}


class Foo {
    private String bar;

    public Foo(String bar) {
        this.bar = bar;
    }

    public String get() {
        return bar;
    }
}


class FinalizeStuff<Foo> extends PhantomReference<Foo> {
    public FinalizeStuff(Foo foo, ReferenceQueue<? super Foo> queue) {
        super(foo, queue);
    }
 
    public void bar() {
        System.out.println("foobar is finalizing resources");
    }
}

테스트 결과는 다음과 같다.

x: true
x: true
x: true
x: true
x: true
x: true
x: true
x: true
x: true
x: true
y: null
y: null
y: null
y: null
y: null
y: null
y: null
y: null
y: null
y: null
true
true
true
true
true
true
true
true
true
true
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources
foobar is finalizing resources

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

 

FileDescriptor의 native 메소드가 어떻게 리눅스와 윈도우에 포팅되었는지 확인한다.

 

1. 자바 소스

java.io.FileDescriptor.java

package java.io;

public final class FileDescriptor {

private int fd;

private long handle;

…..

public native void sync() throws SyncFailedException;

/* This routine initializes JNI field offsets for the class */
private static native void initIDs();

private static native long set(int d);

}

 

2, native 소스

jdk 6u 22  소스 - FileDescriptor_md.c

/*
* @(#)FileDescriptor_md.c 1.4 10/03/23
*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/

#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "io_util.h"
#include "jlong.h"
#include "io_util_md.h"

#include "java_io_FileDescriptor.h"

/*******************************************************************/
/* BEGIN JNI ********* BEGIN JNI *********** BEGIN JNI ************/
/*******************************************************************/

/* field id for jint 'fd' in java.io.FileDescriptor */
jfieldID IO_fd_fdID;

/* field id for jlong 'handle' in java.io.FileDescriptor */
jfieldID IO_handle_fdID;

/**************************************************************
* static methods to store field IDs in initializers
*/

JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) {
    IO_fd_fdID = (*env)->GetFieldID(env, fdClass, "fd", "I");
    IO_handle_fdID = (*env)->GetFieldID(env, fdClass, "handle", "J");
}

JNIEXPORT jlong JNICALL
Java_java_io_FileDescriptor_set(JNIEnv *env, jclass fdClass, jint fd) {
    SET_HANDLE(fd);
}

/**************************************************************
* File Descriptor
*/

JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_sync(JNIEnv *env, jobject this) {
    FD fd = THIS_FD(this);
    if (IO_Sync(fd) == -1) {
        JNU_ThrowByName(env, "java/io/SyncFailedException", "sync failed");
    }
}

 

3. 윈도우 구현

윈도우에서는 SET_HANDLE과 IO_Sync을 다음과 같이 정의하였다.

src/windows/native/java/io/io_util_md.h 파일

SET_HANDLE 매크로에서 input,output,error에 값에 대한 GetStdHandle 함수를 사용한다. (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx

JNIEXPORT int handleSync(jlong fd);

/*
* Setting the handle field in Java_java_io_FileDescriptor_set for
* standard handles stdIn, stdOut, stdErr
*/
#define SET_HANDLE(fd) \
if (fd == 0) { \
return (jlong)GetStdHandle(STD_INPUT_HANDLE); \
} else if (fd == 1) { \
return (jlong)GetStdHandle(STD_OUTPUT_HANDLE); \
} else if (fd == 2) { \
return (jlong)GetStdHandle(STD_ERROR_HANDLE); \
} else { \
return (jlong)-1; \
} \

#define IO_Sync handleSync


src/windows/native/java/io/io_util_md.c 파일

결국 자바의 sync는 윈도우의 FlushFileBuffers(핸들값) 함수를 이용한다.  (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364439(v=vs.85).aspx)

JNIEXPORT int
handleSync(jlong fd) {
/*
* From the documentation:
*
* On Windows NT, the function FlushFileBuffers fails if hFile
* is a handle to console output. That is because console
* output is not buffered. The function returns FALSE, and
* GetLastError returns ERROR_INVALID_HANDLE.
*
* On the other hand, on Win95, it returns without error. I cannot
* assume that 0, 1, and 2 are console, because if someone closes
* System.out and then opens a file, they might get file descriptor
* 1. An error on *that* version of 1 should be reported, whereas
* an error on System.out (which was the original 1) should be
* ignored. So I use isatty() to ensure that such an error was due
* to this bogosity, and if it was, I ignore the error.
*/

HANDLE handle = (HANDLE)fd;

if (!FlushFileBuffers(handle)) {
if (GetLastError() != ERROR_ACCESS_DENIED) { /* from winerror.h */
return -1;
}
}
return 0;
}

 

4. 리눅스 구현

src/sloaris/native/java/io/FileDescriptor_md.c

/* field id for jint 'fd' in java.io.FileDescriptor */
jfieldID IO_fd_fdID;

/**************************************************************
* static methods to store field ID's in initializers
*/

JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) {
    IO_fd_fdID = (*env)->GetFieldID(env, fdClass, "fd", "I");
}

/**************************************************************
* File Descriptor
*/

JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_sync(JNIEnv *env, jobject this) {
    int fd = (*env)->GetIntField(env, this, IO_fd_fdID);
    if (JVM_Sync(fd) == -1) {
    JNU_ThrowByName(env, "java/io/SyncFailedException", "sync failed");
    }   
}

 

src/solaris/native/java/io/io_util_md.h 파일

set_handle은 깔끔히 무시해준다.

/*
* Route the routines through HPI
*/

#define IO_Sync JVM_Sync

/*
* On Solaris, the handle field is unused
*/
#define SET_HANDLE(fd) return (jlong)-1

 

hotspot/src/share/vm/prims/jvm.cpp 파일

JVM_LEAF(jint, JVM_Sync(jint fd))
    JVMWrapper2("JVM_Sync (0x%x)", fd);
    //%note jvm_r6
    return hpi::fsync(fd);
JVM_END

 

JVMWrapper2 매크로는 jvm.cpp에 정의되어 있다. 특별한 일은 하지 않는다.

#define JVMWrapper2(arg1, arg2) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2)

#define JVMCountWrapper(arg) \
static JVMHistogramElement* e = new JVMHistogramElement(arg); \
if (e != NULL) e->increment_count(); // Due to bug in VC++, we need a NULL check here eventhough it should never happen!

class JVMTraceWrapper : public StackObj {
public:
JVMTraceWrapper(const char* format, ...) {
   if (TraceJVMCalls) {
        va_list ap;
        va_start(ap, format);
        tty->print("JVM ");
        tty->vprint_cr(format, ap);
        va_end(ap);
    }
}
};


hpi::fsync() 함수의 원형은 hotspot/src/share/vm/runtime/hpi.hpp, hotspot/src/share/vm/runtime/hpi.h 에 있다.

hotspot/src/share/vm/runtime/hpi.h

typedef struct {
  char *         (*NativePath)(char *path);
  int            (*FileType)(const char *path);
  int            (*Open)(const char *name, int openMode, int filePerm);
  int            (*Close)(int fd);
  jlong          (*Seek)(int fd, jlong offset, int whence);
  int            (*SetLength)(int fd, jlong length);
  int            (*Sync)(int fd);
  int            (*Available)(int fd, jlong *bytes);
  size_t         (*Read)(int fd, void *buf, unsigned int nBytes);
  size_t         (*Write)(int fd, const void *buf, unsigned int nBytes);
  int            (*FileSizeFD)(int fd, jlong *size);
} HPI_FileInterface;

 

hotspot/src/share/vm/runtime/hpi.cpp

//
// C++ wrapper to HPI.
//

class hpi : AllStatic {

static HPI_FileInterface* _file;

static inline int fsync(int fd);

HPIDECL(fsync, "fsync", _file, Sync, int, "%d",
(int fd),
("fd = %d", fd),
(fd));

}


#define HPIDECL(name, names, intf, func, ret_type, ret_fmt, arg_type, arg_print, arg) \
inline ret_type hpi::name arg_type { \
if (TraceHPI) { \
tty->print("hpi::" names "("); \
tty->print arg_print ; \
tty->print(") = "); \
} \
ret_type result = (*intf->func) arg ; \
if (TraceHPI) { \
tty->print_cr(ret_fmt, result); \
} \
return result; \
}

 

hpi.c

static HPI_FileInterface hpi_file_interface = {
sysNativePath,
sysFileType,
sysOpen,
sysClose,
sysSeek,
sysSetLength,
sysSync,
sysAvailable,
sysRead,
sysWrite,
sysFileSizeFD
};

 

hpi_impl.h

int sysSync(int fd);

 

sys_api_td.c

int
sysSync(int fd) {
/*
* XXX: Is fsync() interruptible by the interrupt method?
* Is so, add the TSD, sigsetjmp()/longjmp() code here.
*
* This probably shouldn't be throwing an error and should
* be a macro.
*/
int ret;
if ((ret = fsync(fd)) == -1) {
}
return ret;
}

 

리눅스의 sync 호출은 간단히 리눅스의 fsync 함수를 호출하는 효과와 동일하다.

 

마치며.

재미있는 것은 파일(file), 소켓(socket), 모니터(lock) 모두 jvm.cpp를 거쳐 hpi.hpp 로 내려오게 된다. 파일의 경우는 IO lock/unlock의 개념도 있고. 공부할 내용이 풍부하다..

이제 시간되는 대로 jvm 내용을 찾아 들어가면 될 것 같다.

Posted by '김용환'
,

 

CENTOS 4.7 운영체제를 설치하고 자바 7을 설치했는데, dl (dynamic lib) 에러가 난다. glic 2.4 이상이 설치되어야 jdk 7이 돌아가는 것 같다. Oracle 공식 문서(readme, announcement)에서는 이 부분을 못찾았다.

glic 버전을 확인하니. 2.3이다.

]# ldd --version

ldd (GNU libc) 2.3.4

 

java 1.7.0을 설치했다..

]# java –version
Error: dl failure on line 875
Error: failed /home/www/apps/jdk1.7.0/jre/lib/i386/server/libjvm.so, because /lib/tls/libc.so.6: version `GLIBC_2.4' not found (required by /home/www/apps/jdk1.7.0/jre/lib/i386/server/libjvm.so)

 

java 1.7.0_03을 설치했다. (wget http://download.oracle.com/otn-pub/java/jdk/7u3-b04/jdk-7u3-linux-i586.tar.gz)

]# java –version
Error: dl failure on line 875
Error: failed /home/www/apps/jdk1.7.0_03/jre/lib/i386/server/libjvm.so, because /lib/tls/libc.so.6: version `GLIBC_2.4' not found (required by /home/www/apps/jdk1.7.0_03/jre/lib/i386/server/libjvm.so)

 

 

인터넷을 검색하니. “jdk7-b64 has new dependency on glibc-2.4” 이런 내용도 나오구..

http://mail.openjdk.java.net/pipermail/nio-dev/2009-July/000602.html

 

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

다른 장비는 CentOS 5.3 이다. glibc는 2.5 이다.

]# ldd --version
ldd (GNU libc) 2.5

 

]# ./java -version
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b04)
Java HotSpot(TM) Server VM (build 22.1-b02, mixed mode)

 

리눅스에서 java 7을 사용하려면 glibc 2.4를 쓰는 게 좋을 것 같다.

Posted by '김용환'
,