Java의 List.subList()는 fromIndex와 toIndex를 두어 sub list를 얻는다. 

주의할 점은 toIndex는 포함하지 않는 값이다. fromIndex 이상 toIndex 미만이다. (내가 자주 실수하는 내용이기도 하다..)


List<E>subList(int fromIndex, int toIndex)

간단하게 테스트하면 이렇다. 



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


    list.add("1");

    list.add("2");

    list.add("3");

    list.add("4");

    

    System.out.println("List : " + list);

    

    List<String> subList = list.subList(1,3);

    System.out.println("Sub List : " + subList);



결과는 다음과 같다.


List : [1, 2, 3, 4]

Sub List : [2, 3]



ArrayList의 subList는 SubList라는 내부 클래스를 생성한다. 


    public List<E> subList(int fromIndex, int toIndex) {

        subListRangeCheck(fromIndex, toIndex, size);

        return new SubList(this, 0, fromIndex, toIndex);

    }


private class SubList extends AbstractList<E> implements RandomAccess {

        private final AbstractList<E> parent;

        private final int parentOffset;

        private final int offset;

        int size;


        SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) {

            this.parent = parent;

            this.parentOffset = fromIndex;

            this.offset = offset + fromIndex;

            this.size = toIndex - fromIndex;

            this.modCount = ArrayList.this.modCount;

        }


        메소드 안에..checkForComodification()를 호출하고 있다.

}


checkForComodification()으로 짐작하겠지만, 원래 List를 SubList가 reference(parent)하고 있다. 계속 관련되어 체크한다. 

따라서 아래 코드에서 원본 list의 요소를 삭제하면, java.util.ConcurrentModificationException이 발생한다. 

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


    list.add("1");

    list.add("2");

    list.add("3");

    list.add("4");

    List<String> subList = list.subList(2,4);

    list.remove(1);



Exception in thread "main" java.util.ConcurrentModificationException

at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1169)



관련된 내용은 api에 설명되어 있다. 


http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#subList%28int,%20int%29


The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)




반면, Set.subSet()은 TreeSet에 정의되어 있다. 



NavigableSet<E>subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
Returns a view of the portion of this set whose elements range from fromElement to toElement.
SortedSet<E>subSet(E fromElement, E toElement)


예제는 다음고 ㅏ같다. 

TreeSet<String> set = new TreeSet<String>();

set.add("1");

set.add("2");

set.add("3");

    set.add("4");

    SortedSet<String> subset1 = set.subSet("1", "2");

    SortedSet<String> subset2 = set.subSet("1", true, "2", true);

    

    System.out.println("set : " + set);

    System.out.println("Sub set 1 : " + subset1);

    System.out.println("Sub set 2 : " + subset2);


결과는 다음과 같다. 


set : [1, 2, 3, 4]

Sub set 1 : [1]

Sub set 2 : [1, 2]



TreeSet은 ArrayList의 subList처럼 index(int)가 아닌 "값"으로 subset을 한다. 

Set을 Index(int)로 데이터를 얻어내려면, Set를 List로 바꿔야 한다. 


Tree.subSet은 새로운 NavigationMap.subMap()으로 만든 TreeSet을 생성하기 때문에 subSet() 이후에 데이터가 변경되어도 ConcurrentModificationException이 발생되지 않는다. 


    public SortedSet<E> subSet(E fromElement, E toElement) {

        return subSet(fromElement, true, toElement, false);

    }

   

 public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,

                                  E toElement,   boolean toInclusive) {

        return new TreeSet<>(m.subMap(fromElement, fromInclusive,

                                       toElement,   toInclusive));

    }



No problem!!

TreeSet<String> set = new TreeSet<String>();

set.add("1");

set.add("2");

set.add("3");

    set.add("4");

    SortedSet<String> subset1 = set.subSet("1", "2");

    SortedSet<String> subset2 = set.subSet("1", true, "2", true);

  

    set.remove("1");

    

    System.out.println("set : " + set);

    System.out.println("Sub set 1 : " + subset1);

    System.out.println("Sub set 2 : " + subset2);


Posted by '김용환'
,



mac용 oracle java 8u40부터 잘 살펴보지 않고 설치를 진행하면 무시무시한(?) ask가 설치된다.

http://www.macrumors.com/2015/03/05/ask-adware-java-mac/



실제로 로컬에 설치해보니. ask가 설치되지 않았다. 


http://macnews.tistory.com/3125 에 따르면, 영문 버전에 미국 ip 일 때만 설치된다고 한다. 

Posted by '김용환'
,


play1.2 버전을 jvm7 위에서 사용하고 있다가 jvm 8에서 사용하려다가 실패했다. 단순히 -noverify 만 넣어서 해결되는 문제는 아닌 듯 하다. 컴파일이 되지만, 이상하게 작동되니. 쓰면 안된다.



관련해서 패치등이 있지만, 현재 play 1의 jvm 8 지원은 http://play.lighthouseapp.com/projects/57987/tickets/1800-support-java-8 에서 나와 있는데, 2014년 5월 14일 기준으로  State changed from “resolved” to “inprogress” 로 변경되었다.

즉  jvm 8(java8)에서 동작이 되지 않는다.


(추후 jvm 8이 정식으로 지원이 된다면, 올려보고 apache http 또는 nginx 로그가 정상적으로 응답하는지 꼭 체크해야 한다. 정상 요청에 500에러를 발생시킨다면 아직 jvm8을 적용햘 때가 아니다.) 




그렇다면 play 2 (scala) 는 jvm 8을 사용할 수 있을까?

이 부분은 현재 scala 의 버전과 밀접한 관계가 있다. 나는 play2 스칼라 개발 버전이 있는데. scala 2.11.2, play 2.3을 사용중인데. jvm 8로 실행했더니 이상하게 동작했다. 


확인해보니. 아래 링크를 찾을 수 있었다. scala 2.11에서는 간단한 interop만 될 뿐이고 실제 잘 동작되고 성능까지 보장하는 것은 2.12에서 가능할 것이다. 그것은 2016년 초(정식 릴리즈)에야 가능할 예정이라고 한다.  


http://www.infoq.com/articles/Scala-2-12-Only-Java8

http://scala-lang.org/news/2.12-roadmap



  • It’s important to keep up with the platform, even though Java 8’s MethodHandle-based support for closures may not immediately yield significant performance benefits (definitely reduces bytecode size, and thus likely compilation times, though). For platforms that don’t support Java 8 bytecode yet, two projects exist that rewrite Java 8 invokedynamic bytecode to Java 6 (retrolambda or Forax’s JSR292 backport). I’m not aware of the equivalent for default methods, but it’s feasible.





Posted by '김용환'
,

JAVA 9 기능 확정

java core 2014. 12. 4. 16:17


java 9 기능이 확정되었다.

http://java.dzone.com/articles/oracle-confirms-new-java-9

기존 공지 http://java.dzone.com/articles/java-9-features-announced



http://openjdk.java.net/projects/jdk9/ 에서 확인가능하다.


JDK 9

The goal of this Project is to produce an open-source reference implementation of the Java SE 9 Platform, to be defined by a forthcoming JSR in the Java Community Process.

Status

The JDK 9 repositories have been open for bug fixes and small enhancements for some time now. Features for the release will be proposed and tracked via the JEP Process, as amended by the JEP 2.0 proposal (which will shortly be folded into themain JEP process document).

JEPs targeted to JDK 9, so far

102: Process API Updates
143: Improve Contended Locking
158: Unified JVM Logging
165: Compiler Control
197: Segmented Code Cache
198: Light-Weight JSON API
199: Smart Java Compilation, Phase Two
201: Modular Source Code
211: Elide Deprecation Warnings on Import Statements
212: Resolve Lint and Doclint Warnings
213: Milling Project Coin
214: Remove GC Combinations Deprecated in JDK 8
216: Process Import Statements Correctly
219: Datagram Transport Layer Security (DTLS)
220: Modular Run-Time Images
224: HTML5 Javadoc
231: Remove Launch-Time JRE Version Selection

Last update: 2014/12/01 14:14 -0800



Posted by '김용환'
,



자바 7에서 ISO 8601 을 지원한다.

http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html



LetterDate or Time ComponentPresentationExamples
zTime zoneGeneral time zonePacific Standard TimePSTGMT-08:00
ZTime zoneRFC 822 time zone-0800
XTime zoneISO 8601 time zone-08-0800-08:00


    val dateFormat : DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")

    val iso8601String : String = "2014-12-03T20:27:57+09:00"

    val dateFormatString : Date = dateFormat.parse(iso8601String)


    println(dateFormatString)



결과


Wed Dec 03 20:27:57 KST 2014



그외에도 RFC 822도 지원한다. 


Posted by '김용환'
,

java server에서 threaddump 후, 해당 thread의 nid 값(16진수)를 얻어 10진수의 nid의 값으로 변환하는 쉘 스크립트



$ jstack -l 1111 > threaddump.txt


$ grep 'Memcached IO' threaddump.txt | awk '{print $8}' | awk -F "=" '{printf "%d\n", strtonum($2) }’







Posted by '김용환'
,


jdk를 분석하다가 nio쪽을 보려면 sun.nio에서 소스를 공개하지 않는 오라클 소스에 막힌다. 


oracle jdk 1.7 source는 sun 패키지 (sun.nio.*) 가 포함되어 있지 않다.


만약 sun 패키지의 자바 클래스를 보려면, 아래 소스를 다운받으면 된다.  


http://sourceforge.net/projects/jdk7src/?source=typ_redirect



만약 openjdk의 소스를 보려면 아래 소스를 살핀다. 


https://jdk7.java.net/source.html



Posted by '김용환'
,


AtomicInteger와 같이 AtomicReference를 사용하면 ConcurrentModificationException(이하 CME)를 만날 수 있다. 


AtomicReference(Collection)으로 사용하고 있을 때 내부적으로 AtomicReference 내부의 volatile으로 저장된 Collection value은 set/get에 대해서는 atomic 한다. 하지만 volatile Collection value의 iterator는 atomic하지 않을 수 있다. 따라서 AtomicReference로 감싸고 있는 Collection을 사용할 때는 주의하면서 써야할 필요가 있다. 


Java 7 이전에는 Collections.sort() 메소드 호출시 문제가 있을 수 있었지만, 체크하는 로직이 없었다. CME가 발생되지 않았다.


Collections를 새롭게 구현한 Java8의 Collections.sort() 내에서 소팅하기 전에 count(정확히 AbstractList#modCount, https://docs.oracle.com/javase/8/docs/api/java/util/AbstractList.html#modCount)를 저장하고 소팅하고 나서 저장된 count가 기존에 저장된 것과 비교해서 다르면 CME를 발생하도록 되어 있다. 좀 더 고급스럽게 말하면 CME를 빨리 발생시켜주는 fast-fail iterator를 위한 것이다. 


아래 코드는 Java 7를 포함, 그 이하버전에서는 CME가 발생하지 않았다. 


private AtomicReference<List<String>> myList = new AtomicReference<List<String>>();


public List<String> sort() {

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


List<String> list = this.myList.get();


if (list == null) {

return null;

}

for (String string : list) {

result.add(string);

}


Collections.sort(list);


return result;

}



그러나 Java8에서 Collections.sort() 에서 위에서 설명한 modCount 덕분에 빨리 CME가 발생하게 되었다.


관련해서는 http://stackoverflow.com/questions/10658478/concurrent-modification-exceptions에서 비슷한 내용이 있다.



재미있는 것은 안전한 iterator를 구현한 ConcurrentMap 이다. weak consistent한 iterator를 보장한다.

그리고, set/get/iterator의 안전함이 ConcurrentMap의 thread-safe를 구현하고 있다.  관련 내용은 아래 링크에 자세히 설명되어 있다. 


http://books.google.co.kr/books?id=O6fJBAAAQBAJ&pg=PA28&lpg=PA28&dq=volatile+map+java+iterator+concurrentmodificationexception&source=bl&ots=YhhEoYJIeG&sig=BfmN06mvGZ6EBYaT24JRsro7xV4&hl=ko&sa=X&ei=h_BjVNW1D-bOmwW904HwAQ&ved=0CE4Q6AEwBQ#v=onepage&q=volatile%20map%20java%20iterator%20concurrentmodificationexception&f=false



Posted by '김용환'
,


java 1.5에서 -XX:MaxTenuringThreshold MAX 설정 값이 15로 변경되었다. 그러나 호환성 이슈로 사용을 허락하였다.  Java8에서 -XX:MaxTenuringThreshold MAX 설정의 값을 16 이상의 값을 지정하면 JVM exit가 되도록 수정되었다. 



MaxTenuringThreshold of 31 is invalid; must be between 0 and 15

Error: Could not create the Java Virtual Machine.

Error: A fatal exception has occurred. Program will exit.



-XX:MaxTenuringThreshold=15로 변경했더니 java process 가 잘 동작한다.


Posted by '김용환'
,

WeakHashMap은 thread safe하지 않다. rehash되면서, WeakHashMap의 get() 메소드에서 cyclic reference 가 걸려서 infinite loop가 발생되는 부분이 발생한다.  



       "play-thread-160" prio=10 tid=0x000000000be3c000 nid=0x47ee runnable [0x00000000602e2000] 
  java.lang.Thread.State: RUNNABLE 
       at java.util.WeakHashMap.get(WeakHashMap.java:470) 

     


WeakHashMap의 get()의 다음 entry를 가져오는 부분에서 infinite loop가 발생했다. 


e = e.next ;



해결을 위해서는 WeakHashMap에 thread-safe하게 해주는 Collections.synchronizedMap()를 활용하거나. ConcurrentHashMap을 사용하는 것이 좋다. 저자는 WeakHashMap을 활용하는 의도가 있었기에  Collections.synchronizedMap()을 사용했고 생각보다 큰 부하는 없는 듯 하다.  



* 이해하기 좋은 자료

http://mailinator.blogspot.kr/2009/06/beautiful-race-condition.html
http://blog.wonderwall.me/?p=210

Posted by '김용환'
,