2012년 1월 29일 일요일

Java Garbage Collection에 대해서


1. GC란?

GC는 JVM에서 쓰지 않는 메모리를 자동으로 회수하는 녀석을 말한다.

2. GC의 동작 방법

JVM 메모리 영역 3가지

YOUNG/OLD/PERM

Perm은 Method Area라고도 한다. 객체나 억류된 문자열 정보를 저장하는 곳이다.
여기서 GC가 발생해도 Major GC 횟수에 포함된다.
Perm에서의 GC (http://tuning-java.com/455) String intern한 정보에 대해서 FullGC가 발생한다.



GC 알고리즘

(1) Minor GC

Young은 Eden/Survior로 나뉘게 된다. 그런데 Survivor구역이 두개다 그래서 총 3개의 영역을 Young의 영역으로 보개된다.

YOUNG - EDEN 
            -  SURVIVE1,2
OLD

EDEN : 처음 객체가 생성될시에 상주한다.

Minor Gc가 발생시 아래와 같이 위치를 이동한다.

[MINOR GC 1차 발생시]

EDEN에서 ALIVE하면 SURVIVE1으로 이동한다.
EDEN CLEAR

[MINOR GC 2차 발생시]

EDEN에서 [alive] - > Survivor 2
Survivor  1  [alive] - > Survivor 2
Eden    Clear
Survivor 2 Clear

[MINOR GC 3차 발생시]

객체 생성시간이 오래지나면 Eden과 survivor영역에 있는 오래된 객체들을 Old 영역으로 이동

이런방식의 GC알고리즘을 Copy & Scavenge라고 한다. 이방법은 속도가 빠르며 작은 크기의 메모를 Collection하는데 매우 효과적이다. Minor Gc는 자주 일어나기 때문에 GC에 소요되는 시간이 짧은 알고리즘에 적합.

(2) Full GC(Major GC)

Old영역에서의 GC를 Full GC라고 부르며, Full Gc에 사용되는 알고리즘은 Mark & Compact라는 알고리즘을 사용한다. 

Mark는 전체 객체들의 레퍼런스를 쭉 따라가면서 레퍼런스가 연결되지 않은 객체들을 Mark한다. 이 작업이 끝나면 사용되지 않은 객체들이 Mark되고 Mark된 객체를 삭제한다.

Compact는 mark된 객체로 생기는 부분을 unmark된 사용하는 객체로 매꾸어 버리는 방법이다. 덮어쓰기?!

Full Gc는 속도가 매우 느리다. Full GC가 일어나면 순간적으로 Java app이 STW하기 때문에 FullGc가 일어나는 정도와 Full GC에 소요되는 시간은 App의 성능과 안정성에 큰영향을 준다.


3. GC가 중요한 이유

Minor Gc는 보통 0.5초로 빠르지만 Full GC의 경우 느리다. 5~10초 GC로 인해서 만약 app이 멈췄다가 풀리면 WAS의 경우 요청이 큐에 저장됬다가 한번에 엄청나게 봇물 터지듯이 받게 된다면 DDOS공격같은 효과로 부하가 발생해서 뻗어 버릴수가 있다.



4. 다양한 Old영역에 대한 GC 알고리즘

JDK7기준으로 5가지의 GC방식이 있다.

- Serial GC (쓰지 말자 싱글쓰레드 방식의 GC)
- Parallel GC
- Parallel Old GC(Parallel Compaction GC)
- Concurrent Mark & Sweep GC (이하 CMS)
- G1(Gargage First) GC (아직 시기상조 JDK7이라서 불안)

(1) Serial GC ( -XX:+UseSerialGC)

young 영역은 앞서 설명한 방식을 사용한다. 
Old영역에서 mark - sweep - compact방식을 사용한다.

- 순서     

     1. Old영역에서 살아있는 객체를 mark한다. 
     2. heap의 앞부분부터 확인하여 살아 있는것만 남긴다.(이게 sweep) 
     3. 각 객체들이 연속되어 쌓이도록 힙의 가장 앞부분부터 채워서 객체가 
         존재하는 부분과 객체가 없는 부분으로 나눈다.(Compaction)

Serial은 싱글 cpu 고려 한 알고리즘이라 코어갯수가 적을때 적합하다.(쓰지말자)


(2) Parallel GC( -XX:+UseParallelGC)

기본 알고리즘은 비슷하다. 
다만 싱글쓰레드로 처리하던 Serial GC에 비해서 멀티쓰레드로 처리하기 때문에 Serial보다 빠르다.
Parallel GC는 메모리가 충분하고 코어 갯수가 많을수록 유리하다.
Parallel GC는 Throughtput GC 라고도 불린다.

(3) Parallel Old GC(-XX:+UseParallelOldGC)

Parallel Old Gc는 jdk5 update 6부터 제공하는 Gc 이다. 앞서 Parallel Gc와 비교시 Old영역 Gc알고리즘이 틀리다.

이 방식은 Mark - Summary - Compaction 단계를 거친다.

Summary단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep단계와 다르며, 
약간더 복잡한 단계를 거친다.

(4) CMS GC(-XX:+UseConcMarkSweepGC)



1. initial Mark단계에서는 클래스 로더에서 가장 가까운 객체중 살아 있는 객체만 찾는것으로 끝낸다. 
따라서 멈추는 시간이 매우 짧다. 
2. Concurrent Mark 단계에서는 방금 살아 있다고 확인한 객체에서 참조하고 있는 객체들만 따라가면서 확인한다. 
이단계의 특징은 다른 쓰레드가 실행중인 상태에서 동시에 진행된다는 것이다.
3. Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 
4.Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행한다. 
이 작업도 다른 쓰레드가 실행되는 상황에서 진행된다.

장점 . 

이러하기 때문에 표를 봐도 알수 있듯이 Stop the world 가 짧다.
모든 애플리케이션의 응답속도가 매우 중요할때 Cms GC 를 사용하며 Low latency GC 라고도 불린다.

단점 . 

다른 GC보다 cpu와 메모리를 많이 쓴다.(자원먹는 괴물)
Compaction단계가 기본적으로 제공되지 않는다. (기본적으로는 없다.)

그리고 조각 난 메모리가 많아 Compaction작업을 실행하면 다른  GC 방식의 stw 보다 시간이 더 길기 때문에 Compaction작업이 얼마나 자주, 오랫동안 수행되는지 확인(테스트)해야한다.

(5) G1 GC

이 녀석은 young ,old 영역 구분이 없다.
바둑판으로 각 영역에 객체를 할당하고 GC 를 실행한다. 그러다가 해당영역이 꽉차면 다른 영역에 객체를 할당하고 GC를 실행한다.
즉, young의 세영역(eden,survivor1,2)영역에서 데이터가 old로 이동하던 단계가 없다.
G1 GC 는 장기적으로 말도 많고 탈도 많은 CMS GC를 대체하기 위해서 만들어 졌다.

G1 GC 의 장점은 성능이다. 
위의 녀석들중 최고다 하지만 나온지 얼마안되서 불안하다 시기상조이니 좀 시간이 흐른후 적용하도록하자.


- 출처 : 

http://helloworld.naver.com/helloworld/1329
http://jikime.tistory.com/313

두글을 제가 보기좋게 짬뽕했습니다.

2012년 01월 29일 it 기술 동향

JAVA

tomcat 로드밸런싱
http://nikunjp.wordpress.com/2012/01/28/load-balancing-with-tomcat/

jpa hibernate 프로파일링
http://blog.ej-technologies.com/2012/01/profiling-jpahibernate.html

GC 알아보기
http://worldmodscode.wordpress.com/2011/12/26/practical-garbage-collection-part-1-introduction/

hadoop

hadoop use case
http://www.slideshare.net/datasalt

HTML5

26가지 유용한 html5 튜토리얼
http://www.lava360.com/tutorials/roundup-of-26-truly-helpful-html-5-tutorials/

css3,html5 메뉴들
http://designmodo.com/css3-html5-mega-menus/

분야별 html5 튜토리얼
http://www.underworldmagazines.com/12-html5-tutorials-to-get-you-started/


http://joeycadle.com/blog/article/1/2012/22/01/html5-canvas-and-processing-js

Javascript 

어떻게 js 로부터 데이터를 다운로드 받을까?
http://blogs.adobe.com/cantrell/archives/2012/01/how-to-download-data-as-a-file-from-javascript.html

실시간 소스 수정 페이지
http://rocktronica.github.com/tmpltr/

dart puzzlers
http://www.dartlang.org/articles/puzzlers/chapter-1.html

dart svg logo
http://code.google.com/p/dart/source/browse/#svn%2Fbranches%2Fbleeding_edge%2Fdart%2Fclient%2Fsamples%2Flogo

jquery

jquery html5 base placeholder plugin
http://webcloud.se/code/jQuery-Placeholder/

jquery-ui componet demo
http://hanshillen.github.com/jqtest/#goto_slider

차트 그리기 좋은 raphael 기반 js
http://g.raphaeljs.com/

독특한 슬라이드를 보여주는 highslide.js
http://highslide.com/

CSS3

github리본 만들기
http://www.terrenceryan.com/blog/post.cfm/github-ribbons-in-css

http://www.noupe.com/css/useful-css-snippets-for-your-coding-arsenal.html

Node.js

node.js document
http://yand.info/


http://sourcecodebean.com/archives/callbacks-are-polluting-your-code/1488

android

초간단 앱만들기
http://android-meda.blogspot.com/2012/01/writing-your-first-android-app-body.html

좋은 무료 안드로이드 gui
http://www.bestfreewebresources.com/2012/01/15-fresh-and-free-download-gui-kits-for-android-developers.html

etc.

html5 비디오 지원의 현재
http://www.longtailvideo.com/html5/

자바스크립트의 부흥에 따른 현상 혹은 사건
http://www.2ality.com/2012/01/javascript-pervasiveness.html

jquery moblie과 sencha touch 간단한 설명
http://miamicoder.com/2011/sencha-touch-or-jquery-mobile-read-this-before-you-make-a-decision/

vaadin에 대한 요약 설명
http://blog.porotype.com/post/16575655597/compressing-all-the-vaadin-things

2012년 1월 27일 금요일

Chromium Extension을 통한 Web Intents 맛보기


1. 일단 기존의 chrome 브라우저가 아닌 chromium(http://download-chromium.appspot.com)을 다운받습니다.

2. 다운 받는 동안에 github에 가서 관련 extension(https://github.com/PaulKinlan/WebIntents/tree/master/tools/chrome/extensions/share-services) 을 다운 받습니다.

다운 받아야 할 디렉토리 목록입니다.

3. chromium을 구동합니다.

4. url에 chrome://settings/extensions 입력합니다. 그러면 [환경설정 - 확장 프로그램] 으로 이동하게 됩니다. 여기서 우측의 [개발자 모드] 체크박스를 클릭하여 체크하시면 하단에 [압축해제된 확장 프로그램 로드...] 이라는 버튼이 보이게 됩니다.
클릭하여 앞서 github에서 다운 받은 extenstion중 원하는 폴더(gmail,twitter..)를 선택합니다.
그러면 extension이 등록이 됩니다.



5. 이제 http://examples.webintents.org/intents/share/share.html로 이동하고 하단의 예제로 준비된 share a link 와 share an image를 통해 각각의 share버튼을 클릭하면 chromium 브라우저 url 창 입력란 좌측의 지구본에서 설치한 확장 프로그램을 선택하는 메뉴가 나옵니다. 클릭하면 해당 페이지로 입력란으로 앞서 share한 내용 삽입이 된 상태로 열리는것을 발견할수 있습니다.





JDK 5,7에서 파일속성을 hide로 바꿔보자.


일단 JDK5와 JDK7버전을 두개를 놓고 구현해 보겠다.
JDK7에서 NIO2가 새롭게 등장하면서 파일에 대한 접근 방법이 많이 추가되었다.

일단 JDK5에서는 파일을 숨기는 API 를 별도로 제공하지 않는다.
그러나 약간의 꼼수를 사용한다면 구현은 가능하다.
그리고 OS별로 환경변수 설정방법이 틀리듯이 디렉토리 의 구조적인 모습 또한 틀리다.
(Win,Unix,Linux별로 약간의 차이가 있다.)

Windows의 경우 Runtime.getRuntime.exec("command")를 이용하여 손쉽게 파일을 숨길수가 있다.
예를 들어 이렇게 말이다. 
다른 OS의 경우에도 이런식으로 명령어를 통한 파일에 대한 간섭을 하는것이 일반적이다.

Runtime.getRuntime().exec("attrib +H HiddenFileExample.java");

좀더 구체적으로 살펴 보자면 이런식이다.

void setHiddenProperty(File file) throws InterruptedException, IOException {
    Process p = Runtime.getRuntime().exec("attrib +H " + file.getPath());
    p.waitFor();
}

여기에서 p.waitFor()는 필수적인것은 아니다. 하지만 이 해당 명령어가 실행이 완료될동안 안정성을 보장받기 위해서 는 이 부분을 명시해야한다.
(명령어를 실행하고 나서는 os쪽으로 통제가 떠나기 때문에 안정성이 보장되지 않는다.)


 JDK7부터는 파일을 통제하기 쉽게 새로운 API들이 추가 되었다.

Path에 접근하고 위치를 파악할수 있는 일종의 선택자 같은 기능을 가진 API를 제공하게 되었다.

위의 내용을 JDK7의 새로운 API를 활용해서 바꿔보자.

Path path = FileSystems.getDefault().getPath("directory", "hidden.txt");
Boolean hidden = path.getAttribute("dos:hidden", LinkOption.NOFOLLOW_LINKS);
if (hidden != null && !hidden) {
    path.setAttribute("dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS);
}

Runtime.exec()를 통한 기존의 방법이나 새로운 java.nio.file 패키지를 활용하거나 둘다 파일에 대한 컨트롤이 가능하다.

2012년 1월 26일 목요일

JVM이란 무엇인가?


Java Virtual machine 에 들어가기 앞서 : - 
이름에서도 알수있듯이 물리적인 기계와 소통을 할수있도록 자바프로세스를 실행시키는 가상 머신이다. 

JRE는 java 기반의 API 와 JVM를 포함하고 있다. 
  • 당신의 java code를  java byte code로 컴파일하여 물리적인 기계가 이해할수 있게한다.
  • 오브젝트의 생성이나 가비지컬렉션같은 오브젝트 관리를 핸들링한다.

JVM 의 각각 구성요소
  • Heap memory:-
  • 힙 메모리는 JVM 머신 의 한 부분으로 오브젝트를 저장한다.
  • 오브젝트의 종류로는 멤버(전역)변수, 생성자, 메소드들이다.
  • 힙메모리의 기본크기는 64mb로 -Xmx(최대 힙사이즈), -Xms(초기 힙사이즈) 옵션을 이용하여 변경할수가 있다.
  • 참조하는 객체의 생명주기는 개체가 참조가 해제 됬을경우로써,
  • 가비지 컬렉션이 몇몇 알고리즘을 이용하여 오브젝트를 제거한다.
  • Stack Memory:-
  • 로컬(지역) 변수들이 상주해있다. 로컬변수의 생명주기는 메서드가 실행되는 일시적인 순간이다. 함수나 메서드가 종료되는순간 값이 제거된다.
  • 이것은 후입선출의 방식을 사용한다.
  • Method section:-
  • 이 구역은 현재 실행중인 바이트 코드들이 존재한다.
  • 바이트 코드가 실행된후, 포인트는 다음 바이트 코드를 가리킨다.
  • registers:-
  • 어떤 스택의 변수에 의해 활용된다.
각기 벤더에 따라서 다양한 JRE가 제공된다.
  • 1.JRocket JVM  BEA Systems가 개발한 JVM이다. (현재 오라클)
  • 2. Java HotSpot VM Sun Microsystems가 개발한 JVM이다.(현재 오라클)

Java 8의 Project Lambda를 알아보자.


Java 8 - Project Lambda

project lambda란?: Project lambda 는 lambda표현식을  자바 문법에 맞춰서 사용가능하게 만드는 프로젝트입니다.
lambda 표현식은 Lisp같은 함수형 언어 문법을 사용하는것입니다.
대표적으로 Groovy는 closures로 대표되는 lambda표현식을 지원하게 해주는 java 계열의 언어입니다.

그렇다면 lambda 표현식이란 무엇인가?
그것은 코드 블록간에  값을 할당하거나 메서드에 인수로 할당하거나 다른 lambda 표현식으로 전달할수 있게 해주는것입니다.
해당 코드는 필요할때마다 불러올수가 있습니다.
이것의 주 모티브는 JAVA에서 API사용자로 하여금 코드에 필요한 특정 API사용하여 보일러 플레이트 코드를 제거하는것입니다.
그러나 그건 단지 자바 구문 요구사항을 내부 클래스를 사용하여 종료하는것에 불과한것입니다.
이런 대부분의 일반적인 API는 자바 쓰레딩 API로써, 우리가 새로운 쓰레드를 실행할때 필요한 API를 말하는것입니다.
그러나 결국 Runnable이 implementing(구현) 되야합니다.


이 규격은 아직도 아직도 지속적으로 발전하고 있다. 

함수형 인터페이스 : 자바 스펙 개발자들은 JVM 스펙을 좀처럼 수정할려 하지 않습니다. 마찬가지로 이 경우에도 그렇습니다.
lambda의 경우 이러한 JVM 스펙에 수정없이 구현할수 있도록 정의하고 있습니다. 
그러므로 당신은 1.8 버전의 소스 클래스 코드를 목표하는 1.5버전에 쉽게 컴파일 할수 있습니다.
그래서 lambda 코드는 하나의 메소드를 가진 인터페이스를 implementing한 익명클래스에 보관됩니다.


물론 그 인터페이스는  하나 이상의 메서드를 가질수는 있지만 그것은 하나의 메서드를 정의하는 클래스에 implementable 되야 합니다.

그러면 코드를 통해서 lambda에 대해서 살펴 보도록 합시다.

//단순한 경우로, 오직 하나의 메서드만을 가지고 있다.
//이것은 함수형 인터페이스이다.
public interface Test1{
    public void doSomething(int x);
}

//두개의 메서드를 가지고 있다 하지만 toString()의 경우는 기존에 있는것이다.
//어떤 오브젝트던지간에 java.lang.Object의 서브클래스이다.
//이것은 함수형 인터페이스이다.
public interface Test2{
    public void doSomething(int x);
    public String toString();
}

//이 메서드는 Test1의 메서드를 Test3에 오버라이드 한것이다. 
//그래서 아직은 함수형 인터페이스이다.
public interface Test3 extends Test1{
    public void doSomething(int x);
}

//이것은 함수형 인터페이스가 아니다. 
//이 경우, 명시적으로 두가지 메서드를 구현해야한다.
public interface Test4 extends Test1{ 
    public void doSomething(long x);
}


Lambda 표현식: 
자바8에서 지원되는것으로 lambda표현식은 단지 함수형 인터페이스들을 익명 클래스에 implement하는 문법과 구현 방법이 다릅니다.
lambda 구문을 구현하는 방법을 보도록합시다.

argumentList -> body 

저 argumentList는 단순히 자바 메서드 argument 리스트이다.
만약에 하나의 argument만 있다면 ()는 선택사항입니다. 또한 agument의 형에 대한 선언 부분도 선택사항입니다. 
이경우 형이 없습니다. 자동으로 추정합니다.
body의 경우 표현적 body와 코드 블락 body으로 두개의 타입이 있습니다.
표현적 body의 경우 단순히 자바 표현에 따라 값을 리턴해줍니다.
코드 블락 body의 경우 메서드 바디만을 포함하고 있습니다.
이 코드 블락 body는 필수적으로 중괄호 한쌍(]])을 포함해야 합니다.

하나 Thread에 lambda 구문을 구현한 예제를 봅시다.

//이 쓰레드는 Hello를 출력하게 됩니다.
new Thread(() -]]> { while(true){ System.out.println("Hello"); }}).start();


위의 예제에 이어서 또 다른 예제를 보자면

public interface RandomLongs{
    public long randomLong();
}

RandomLongs randomLongs = () -]]> ((long)(Math.random()*Long.MAX_VALUE));
System.out.println(randomLongs.randomLong());


Generic 과  lambda:  그러나 만약에 제네릭 메서드를 lambda에 implement한다면? 이에 표준 개발자들은 형 파라미터를  타입 인자전에 정의할수 있도록 멋진 구현식을 제공했습니다.

예를 보자면 이렇습니다.

public interface NCopies{
    public <T extends Cloneable]]> List<T]]> getCopies(T seed, int num);
}

//제네릭 메서드의 인자에 타입 유추기능을 제공한다.
NCopies nCopies = <T extends Cloneable]]> (seed, num) -]]> { 
                    List<T]]> list = new ArrayList<]]>(); 
                    for(int i=0; i<num; i++) 
                        list.add(seed.clone()); 
                    return list;
                };


요점 요약 : 람다 표현식에 의해 구현된 실제 인터페이스 및 방법은 그것이 사용되는 시점에 따라 달라집니다.
컨텍스트도 할당 작업의 존재 또는 메서드 호출에서 매개 변수의 전달하여 설정하실 수 있습니다
컨텍스트 없이는  lambda 표현식은  의미가 없습니다. 단순히 lambda 표현식에 직접 하나의 메소드를 호출하는 목적이라면 맞지 않습니다. 

컴파일 에러를 내게되는 예를 들어보자면

public interface NCopies{
    public <T extends Cloneable]]> List<T]]> getCopies(T seed, int num);
}

//이 코드는 컴파일 에러를 냅니다.
//lambda에 context가 없어서 그 의미가 없기 때문입니다.
(<T extends Cloneable]]> (seed, num) -]]> { 
                    List<T]]> list = new ArrayList<]]>(); 
                    for(int i=0; i<num; i++) 
                        list.add(seed.clone()); 
                    return list;
                }).getCopies(new CloneableClass(), 5);


그래서 옳게 쓰려면 아래와 같이 바꿔줘야합니다.

NCopies nCopies = <T extends Cloneable]]> (seed, num) -]]> { 
                    List<T]]> list = new ArrayList<]]>(); 
                    for(int i=0; i<num; i++) 
                        list.add(seed.clone()); 
                    return list;
                };
                
nCopies.getCopies(new CloneableClass(), 5);

JAVA의 기본적인 최적화 팁


1. 불필요한 캐스팅을 피하자.

신입 개발자의 경우 참조 데이터형을 변환하는 경우 캐스팅을 한다.
대부분의 캐스팅은 몇몇 기본 클래스 타입의 자식클래스로 모든 객체의 타입을 제네릭 형태의 프로그래밍을 할수있다.

그러나 신입 개발자들의 경우 항상 그들이 잘못 캐스팅했다는것을 모르고 VM으로 하여금 불필요한 부하를 만든다.

예를 들어 보자:

1
class MyCastingClass {
2
    public Object myMethod() {
3
        String myString = "Some data";
4
        Object myObj = (Object) myString;
5
        return myObj;
6
    }
7
}

좋다. Object myObj = (Object) myString;  구문을 보면 불필요한 캐스팅하고 있는것을알수있다. string 을 Object 형으로 변환하는것인데, String은 Object의 자식형으로 부모의 성향을 다 가지고 있는데 굳이 캐스팅을 할 필요는 없는것이다.
그리고 캐스팅은 자바 컴파일러의 안전검사를 무시하고 오류를 일으킬수도있다.

개선해 보자:

1
class MyCastingClass {
2
    public Object myMethod() {
3
        return "Some data";
4
    }
5
}

자 이제 깔끔하게 수정이 되었다. 무엇보다 코드가 줄어서 가독성도 향상되었고 컴파일 규칙으로부터 안정적이고 메모리의 오버헤드 발생또한 없앴다. 아주 좋다.

2. 일반적인 하위 표현식의 제거

이것은 일반적인 실수로 같은 하위표현식을 중복해서 사용하는경우가 있다. 

예를 들어보자 :

1
int someNumber = (someValue * number1 / number2) + otherValue;
2
int someNumber2 = (someValue * number1 / number2) + otherValue2;

이 경우 계산을 각각 객체마다 실행하기 때문에 부하가 두배로 들게된다. 그리고 이 경우는 그나마 두번 정의했기때문에 부하가 적을지 몰라도 만약에 점점 사용이 늘어 나게 된다면 더욱 더 부하 발생의 심각도 는 심해질것이다 그렇다면 이러한 것을 수정해 보자.

1
final int constantCalculation = (someValue * number1 / number2);
2
int someNumber = (constantCalculation) + otherValue;
3
int someNumber2 = (constantCalculation) + otherValue2;

아주 좋다. 중복되는 부분을 하나의 변수로 따로 선언하고 final을 선언하여 상수화 했다. 이로써 심플한 최적화가 완료되었다.

3. 상수가 아닌 string 대신에 StringBuffer나 StringBuilder를 이용하자.

String 변수는 immutable 변수이기 때문에 변화가 일어날시 오버헤드를 만들게 된다. 당신이 새로운 행동을 정의할때마다 컴파일러는 인터프리트에게 명령을 내려 변경을 해야하기 때문이다.
String 변수가 변경될때 변수 공간에 새로운 것을 만들어야한다. 그후에도 이러한 경우에도 마찬가지이다.
이 부하는 String 대신에 StringBuffer나 StringBuilder를 사용하여 해결할수 있다.
그럼 예를 들어보자:

1
class MyStringClass {
2
    public void myMethod() {
3
        String myString = "Some data";
4

5
        for (int i = 0; i < 20; i++) {
6
            myString += getMoreChars();
7
        }
8
    }
9
}  

이것은 StringBuffer 클래스를 존재하여야 하는 이유이다.
StringBuffer는 가변적인 객체로써 잦은 길이의 변경이나 추가 들에 용의한 객체를 제공해주는 클래스이다.
StringBuffer와 StringBuilder의 차이는 StringBuffer는 동기화시에 항상 안전성을 보장합니다. 그에 반해 StringBuilder는 동기화서 안정성이 보장되지 않습니다. 
다만 장점으로 보자면 로컬 변수로 사용시 더 좋은 성능을 보여줍니다.
위의 예를 변경시켜보자:

01
class MyCastingClass {
02
    public Object myMethod() {
03
        String myString = "Some data";
04
        StringBuffer myBuffer = new StringBuffer();
05

06
        for (int i = 0; i < 20; i++) {
07
            myBuffer.append(getMoreChars());
08
        }
09
        myString = myBuffer.toString();
10
    }
11
}

StringBuffer 사용하여  컴파일러로 하여금 변동시마다 부하를 걸리게하는것을 해소 할수있게 되었다. 그후, toString()을 이용하여 String으로 변동하여 myString에 삽입함으로써 원래 소스코드의 목적을 훼손하지 않고 처리할수 있게 되었다.

4. 단순한 Boolean 연산자를 사용하자.

만약에 하나의 연산에 두가지 조건을 정의했을경우 단락회로 검사시 첫번째 조건이 참일시 컴파일러는 두번째 조건은 확인하지 않기 때문에 평가하는 시간을 줄일수가 있다.
예를 들어 ||에 의한 or를 사용시에는 첫번째 조건이 참이면 두번째 조건은 연산하지 않기 때문에 속도가 빨라집니다. 그러나 | 를 사용한 or시에는 두가지 조건을 다 연산하기 때문에 || 경우에 반해 속도가 느려집니다. 그와 같이 && 경우도 앞의 문장이 false 인 경우 두번째 조건을 연산하지 않게 됩니다.

그럼 소스코드를 통해서 확인 해 봅시다.

1
class MyShortCircuitClass {
2
    public void myMethod() {
3
        if (someValue.equals("true") | someValue.equals("false")) {
4
            System.out.println("valid boolean");
5
        }
6
    }
7
}

단순하게 참과 거짓을 통해 결과를 출력하는 것이지만, 이 경우에는 두가지 조건을 다 연산을 하게된다. 그렇다면 이것을 수정해보자.

1
class MyCastingClass {
2
    public void myMethod() {
3
        if ("true".equals(someValue) ||"false".equals(someValue)) {
4
            System.out.println("valid boolean");
5
        }
6
    }
7
}

| 를 || 로 변환함으로써 우리가 목표한 바를 달성했다.

의미 
Short circuit?
&&
and
yes
&
and
no
||
or
yes
|
or
no
표를 통해서 앞서의 경우와 유사한 경우를 판단해서 부하를 줄여보자.

5. 단순한 empty확인시에는 equals() 대신에 length()를 사용하자.

몇몇 개발자들은 String의 문자열의 유무를 equals()를 통해서 확인하곤 한다.

이와같이 :
1
class MyStringClass {
2
    public boolean myTestMethod() {
3
        return myString.equals("");
4
    }
5
}
이것의 문제는 equals()를 사용시 높은 부하가 발생하게 된다. 
이 이슈는 equals()를 구현시 기본적으로 같은 객체 클래스를 두개의 객체가 참조하여 테스트를 하게 디자인 되있기 때문에 생기는 것이다.
결론적으로 length()또는 isEmpty()의 경우 단순히 같은 객체 타입으로 참조포인트를 놓고 테스트를 하게된다. 
이것은 단순히 빈값 테스트를 하기에는 효율적이다. 
자 위의 내용을 바꿔보자.

1
class MyCastingClass {
2
    public void myMethod() {
3
        public boolean myTestMethod() {
4
            
// which really does myString.length() == 0 behind the schenes
5
            return myString.isEmpty();
6
        }
7
    }
8
}

length()를 사용하여 컴파일러에게 객체 참조 부하를 주지 않고 string의 빈값을 테스트 할수 있는 효율적인 메서드를 제공하게 되었다.

기억해야한다. 코드의 최적화는 조잡한 트릭과 읽기 힘든 코드가 필요없는데서 발생한다.
종종 단순한 변화가 프로세싱 시간을 줄여주고 코드를 좋게 만들어준다.
위의 경우를 사용하여 코드의 최적화를 해보자.