2017년 1월 26일 목요일
[clojure] 클로저 소개글
클로저에 대해 한 번 써보았다.
좀 약파는 글이기는 한데 재미있었다.
http://blog.naver.com/andwise/220920969200
2017년 1월 22일 일요일
[clojure][design pattern] 상태패턴 , 비지터 패턴
clojure design pattern
출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html#command
3. 상태패턴
유료/무료 사용자 차이, 잔액 계산, 유료 전환JAVA
1.두개의상태를 enum으로 만든다.
public enum UserState { SUBSCRIPTION(Integer.MAX_VALUE), NO_SUBSCRIPTION(10); private int newsLimit; UserState(int newsLimit) { this.newsLimit = newsLimit; } public int getNewsLimit() { return newsLimit; } }
2. 유저를 만든다.
public class User { private int money = 0; private UserState state = UserState.NO_SUBSCRIPTION; private final static int SUBSCRIPTION_COST = 30; public List newsFeed() { // enum안에 들어있는 리미트 값으로 보여준다. return DB.getNews(state.getNewsLimit()); } public void pay(int money) { //돈을 넣어서 잔액을 지불하고 임계점을 넘으면 유료회원이 된다. this.money += money; if (state == UserState.NO_SUBSCRIPTION && this.money >= SUBSCRIPTION_COST) { // buy subscription state = UserState.SUBSCRIPTION; this.money -= SUBSCRIPTION_COST; } } }
CLOJURE multimethod
(defmulti news-feed :user-state) (defmethod news-feed :subscription [user] (db/news-feed)) (defmethod news-feed :no-subscription [user] (take 10 (db/news-feed))) ** defmulti는 무엇을 플래그로 멀티메소드를 만들 것인가. (:user-state으로 분기한다. (def user (atom {:name "Jackie Brown" :balance 0 :user-state :no-subscription})) (def ^:const SUBSCRIPTION_COST 30) (defn pay [user amount] (swap! user update-in [:balance] + amount) (when (and (>= (:balance @user) SUBSCRIPTION_COST) (= :no-subscription (:user-state @user))) (swap! user assoc :user-state :subscription) (swap! user update-in [:balance] - SUBSCRIPTION_COST))) (news-feed @user) ;; top 10 (pay user 10) (news-feed @user) ;; top 10 (pay user 25) (news-feed @user) ;; all news
4. 방문자 패턴 (Visitor pattern);
public abstract class Item{ abstract void accept(Visitor v); }
class Message extends Item { void accept(Visitor v) { v.visit(this);} }
class Activity extends Item { void accept(Visitor v) { v.visit(this);} }
// 비지터
public interface visitor {
void visit(Activity a);
void visit(Message m);
}
class PDFVisitor implements Visitor {
public void visit(Activity a) { System.out.println("PDFExporter.export(a) - Activity");
public void visit(Message m) { System.out.println("PDFExporter.export(a) - Message");
}
class ExcelVisitor implements Visitor {
public void visit(Activity a) { System.out.println("ExcelExporter.export(a) - Activity");
public void visit(Message m) { System.out.println("ExcelExporter.export(a) - Message");
}
main {
Item i = new Message();
Visitor v = new PDFVisitor();
i.accept(v);
}
더블디스패치패턴이 생겨난 이유는(추측컨데) 인자 타입체크를 하지 않기 때문이다. 예를들어
(비지터 패턴을 사용하지 않는 경우 가정 출처링크 참고)
Item i = new Message();i.accept()를 실행하면 그것이 Activity인지 Message인지 알 필요가 없다. 왜냐하면 이놈은 타입체크가 이루어지기 때문이다. 하지만 아래 놈은 다르다.
Format f = new PDF(); //여기서 Format은 PDF,EXCEL,GIF를 모두 다루는 놈이라 가정하자. i.accept(f)될거라 생각하지 말자.
Format f는 인자로 들어와서 타입체크가 되지 않았다. (인자는 타입체크를 하지 않는다는 것이 중요하다.) 그래서 더블디스패치로 이중 타입체크를 하는 것이다. 하는 방법은 아주 단순하다.
i.apply(f); // 만약 이런 메소드를 실행하려 하는데 i는 타입체크가 되었고 f는 인자이기 때문에 타입체크가 안되있다면 이 메소드를 바로 실행하는 것이 아니다. f.apply(i); // 이렇게 바꿔서 f도 타입체크를 하도록 하는 것이다. 그러면 두번 타입체크가 되는 것이다.어쨋든 Visitor패턴으로 잘 만들었다.
clojure
(defmulti export (fn [item format] [(:type item) format])) {:type :message :content "Say what again!"} {:type :message, :content "Say what again!"} {:type :activity :content "Quoting Ezekiel 25:17"} {:type :activity, :content "Quoting Ezekiel 25:17"} (defmethod export [:activity :pdf] [item format] (println "exporter/activity->pdf" item)) (defmethod export [:activity :xml] [item format] (println "exporter/activity->xml" item)) (defmethod export [:message :pdf] [item format] (println "exporter/message->pdf" item)) (defmethod export [:message :xml] [item format] (println "exporter/message->xml" item)) (defmethod export :default [item format] (throw (IllegalArgumentException. "not supported")))===============================================
(export {:type :message } :xml) exporter/message->xml {:type :message} nil user=> (export {:type :message } :pdf) exporter/message->pdf {:type :message}===============================================
(하지만
:pdf
와 :xml
사이에는 아무런 상하 관계(hierarchy)가 존재하지 않네요. 단순히 키워드일 뿐이잖아요?)hierarchy만들기
(derive ::pdf ::format) (derive ::xml ::format) (isa? ::xml ::format) (isa? ::pdf ::format) (defmethod export [:message ::pdf] [item format] (println ":message ::pdf" item)) (defmethod export [:message ::format] [item format] (println ":message ::FORMAT!!!!" item)) (export {:type :message :content "SAYHIHIHIHI"} ::pdf) :message ::pdf {:type :message, :content SAYHIHIHIHI} nil user=> (export {:type :message :content "???????????"} ::xml) :message ::FORMAT!!!! {:type :message, :content ???????????} nil
::xml을 구현하지도 않았는데 만들어졌다. FORMAT으로 거슬러 올라가서 일이 실행된것!!
** 더블디스페치를 지원하면 비지터패턴은 의미가 없다.
[clojure][design pattern] 클로저 디자인패턴 - 전략패턴
출처::http://clojure.or.kr/docs/clojure-and-gof-design-patterns.html#command
2. 전략패턴(Strategy Pattern)
이름순으로 정렬(단, 유료고객은 먼저)(def users [{:subscription true :name "Y"} {:subscription true :name "Z"} {:subscription false :name "Z"}]) (sort (comparator (fn [u1 u2] (cond (= (:subscription u1) (:subscription u2)) (neg? (compare (:name u1) (:name u2))) (:subscription u1) true :else false))) users)위 소스는 comparator를 만들고 하는 것이 아주 비슷하다.
아래 소스를 봐보자.
(sort-by (juxt (complement :subscription) :name) users) ;; forward sort (sort-by (juxt :subscription :name) #(compare %2 %1) users) ;; reverse sort워워 하나씩 보자.
(sort-by count ["aaa" "bb" "c"]) ("c" "bb" "aaa")juxt 이녀석은 뭘까? (juxtaposition : 병렬, 병치)
((juxt a b c) x) == [(a x) (b x) (c x)]
(for [user users] ((juxt :subscription :name) user)) ;; ([true "Y"] [true "Z"] [false "Z"]) (for [user users] ((juxt (complement :subscription) :name) user)) ;; ([false "Y"] [false "Z"] [true "Z"])
complement (보수 말하는건가) : 함수를 받아서 true/false의 반대값을 내보냄
(def odd?? (complement even?)) (odd?? 3) true이걸 먼저 알아보자
(sort-by (juxt (complement :subscription) :name) users) ;; forward sort user=> ((complement :subscription ) {:subscription "HI"}) false user=> ((complement :a ) {:b "HI"}) true
두 줄로 만들어지는 것이 신기하다. juxt는 엄청난 함수 인 것 같다. 나중에 쓸일이 많을 것 같다.
컴파일 00
1. 구문 해석
2. 의미 해석
3. 중간 표현의 생성
4. 코드 생성
1. 구문 해석
소스 코드의 해석을 파스(Parse) 또는 구문해석(syntax analyzing)이라고 한다. 또한 소스 코드를 파스하는 프로그램 모듈을 파서(parser) 혹은 구문 해석시(syntax analyzer)라 한다. 그러면'컴퓨터가 이해하기 쉬운 형식'은 어떤 것일까. 그것은 구문 트리(syntax tree)라고 하는 형식이다. 구문 트리는 이름 그대로 트리 구조로 소스 코드의 문법 구조를 그대로 옮겨 놓은 구조이다.
2. 의미 해석
소스 코드를 Parse해서 구문 트리가 만들어졌으면, 다음에는 구문 트리를 해석해서 필요 없는 부분을 없애기도 하고 정보를 추가하기도 하여 추상 구문 트리(AST, abstract syntax tree) 데이터 구조를 만든다. 이 처리를 의미 해석(semantic analysis)이라 한다.
- 어떤 변수가 로컬 변수인지 글로번 변수인지 구별하기
- 변수 선언과 참조를 연결 짓기
- 변수나 식의 타입을 체크하기
- 변수를 참조하기 전에 초기화하고 있는지 조사하기
- 리턴 값이 있는 함수가 값을 제대로 반환하고 있는 체크하기
3. 중간 표현 생성
추상 구문 트리가 생기면, 다음에는 컴파일러 내부에서만 사용되는 중간 표현(IR, intermediate representation)구조로 추상 구문 트리를 변환한다. 일부러 중간 표현을 생성하는 가장 큰 이유는 여러 종류의 프로그래밍 언어나 기계어에 대응하기 위해서다.
예를들어 CPU에 따라 다른 컴파일러가 필요하다. (중간 표현이 없다면) 그러니 상황에 따라 모든 컴파일러를 만들어야 한다면 엄청난 수의 컴파일러가 필요하다. 그러니 만약 모든 프로그래밍 언어가 공통의 중간 표현으로 변환하면 1개의 언어, 1개의 CPU에 대응하는 코드는 각각 1개만 있으면 된다. 그래서 여러 종류의 언어나 CPU에 대응하는 컴파일러는 중간 표현을 사용하는 것이 좋다. 예를 들면 GCC는 RTL(register transfer language)이라는 중간표현을 사용한다.
4. 코드 생성
마지막으로 중간 표현을 어셈블리 언어로 변환한다. 이 단계를 코드 생성(code generation)이라고 한다. 코드를 생성하는 프고그램 모듈이 코드 제너레이터(code generator)이다. 코드 생성 포인트는 프로그래밍 언어와 어셈블리 언어의 차이를 얼마나 줄일 수 있는가 하는 점이다.
5. 최적화
실제 컴파일러에는 코드를 최적화(optimization)하는 단계까 포함된다.
[clojure]nil 체크
user=> (if nil true false)
false
user=> (if false true false)
false
user=> (if -1 true false)
true
user=> (if 0 true false)
true
user=> (if "false" true false)
true
하지만 비어있는 컬렉션컬렉션은?
user=> (if [] true false)
true
이럴수가. 하지만 이렇게 하면 된다.
user=> (if (not (empty? [])) true false)
false
이게 뭔고생인가. Joy Of Clojure에서는 다른 방법을 제시한다. 바로 (seq s)이다.
user=> (defn print-seq [s]
#_=> (when (seq s)
#_=> (prn (first s))
#_=> (recur (rest s))))
#'user/print-seq
user=> (print-seq [])
nil
여기서는 rest를 쓰고 next를 쓰지 말라고 하는데 왜인지 한번 보자.
user=> (defn my-seq [s]
#_=> (when (seq s)
#_=> (prn (first s))
#_=> (recur (next s))))
#'user/my-seq
user=> (my-seq [])
nil
음... 이게 문제는 아닌 것같다.
user=> (next [])
nil
user=> (rest [])
()
이게 문제인 것 같다.
rest는 시퀀스가 비어있는지 아니던지 nil를 리턴하지는 않는다.
next는 바로 nil을 리턴한다.
우리는 각 반복 시점마다 (seq s)를 쓴다 그러니 시퀀스를 다루는 rest를 넣는게 더 조합이 잘되있다고 보는 것이다. (nil이 나오지 않게 하려 하는 점은 좋은 생각이다.)
** 여담으로 컬렉션을 순회할 때는 재귀보다는 doseq를 사용하는 편이낫다고 한다. 다만 doseq를 사용하면 위철머 살펴 보는 것은 힘들다. 클로저에서 do로 시작하는 구문 (doseq, dotimes, do 등)은 부수 효과를 일으키기 위해 사용하기 때문에 보통 결과로 nil을 리턴한다.
ORA-01722: 수치가 부적합합니다 [해결]
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
이 에러는 딱 봐도 숫자가 문제가 있다는 에러인데, 확인하고 고쳐달라는 부탁(?) 오더(?)를 받았다.
그런데 아무리 봐도 뭐가 문제 인지 알 수가 없었다. 왜냐하면
//가상의 테이블
======================================
SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(USER_NO,3)=1;
======================================
이건 잘 나오는데
======================================
SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(USER_NO,3)=1 )
======================================
이건 안되는 것이다.
혹시해서
DESCRIBE를 이용해서 테이블을 확인했다. USER_NO가 VARCHAR임을 확인하고
TO_NUMBER를 한번 붙여보기로 했다.
======================================
SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND MOD(TO_NUMBER(USER_NO),3)=1 )
======================================
그래도 안된다
혹시해서 한 번더!
======================================
SELECT count(*) FROM ( SELECT EMAIL, MOBILE, MEMBER_NO FROM INFO Where EMAIL is not null AND USER_CODE=00000000 AND TO_NUMBER(MOD(TO_NUMBER(USER_NO),3)) =1 )
======================================
된다. 오라클에 대해 잘 몰라서 어떤 일이 일어났는지는 잘 모르겠다. VARCHAR에서 TO_NUMBER 했으면 숫자가 된 것이 아닌가? 그러면 count(*)는 안되고 전부 찍는 건 되는 거지? 좀 더 알아봐야할 것 같다.
clojure 파일 쓰기 (lazy sequence를 만난 순간)
그래서 숫자를 이용해서
0@test.com 1@test.com 2@test.com 3@test.com 4@test.com . . . 1000000@test.com이렇게 만드는 것인데 클로저로 하니까 아무리 해도 안되서, 결국 자바로 만들었다.
나는 정말 많이 부족한가보다... 라고 생각하고 있던 찰나
일이 끝나고 저녁 먹으면서 한번 REPL을 켜서 테스트해봤다.
(spit "/Users/tom/a.txt" (clojure.string/join "\n" (flatten (for [x (range 0 1000000)] (conj '() (str x "@test.com"))))))잘된다.
클로저를 하지 말아야 하나...
해당내용을 구글 클로저 그룹스에 물어봤는데 다들 멋지게 풀어 선보이기 시작했다.
(spit "a.txt" (clojure.string/join "\n" (for [x (range 1000000)] (str x "@test.com, " x)))) (spit "a.csv" (with-out-str (dotimes [n 1e6] (println (str n "...@test.com, " n)))))거기서 나온 조언은 lazy시퀀스가 반환될 때, dall함수나 dorun함수를 이용해서 강제로 실행시켜야 한다는 것이다.
ex)
(def foo (map println [1 2 3])) #'user/foo (def foo (doall (map println [1 2 3]))) 1 2 3 #'user/foolazy sequence에 대한 개념이 부족했었다. 정말... 아직 멀었다.
[clojure][macro] 매크로사용하기 00
코드 자체가 데이타(자료구조)이기 때문에 이것을 맘대로 바꿀 수 있다.
어쨋든 이게 뭐라는 거야
제대로 알아보자. (REPL을 예로들자)
REPL : Read-Eval-Print loop
Read : 문자단위표시방식 (character-based representation, typically an input stream) 을 받아서 클로저 자료구조로 변환한다. 그래서 Read phase(읽기상태)의 아웃풋은 data(데이타)이다. 이 데이터는 두번째 상태에서 평가된다. (데이터로!)
EVEL : 어떻게 EVAL에서 code-as-data에서 data-as-code로 변할까 (자료구조안에 있는 코드들이 코드로 만들어진 데이터)
read는 표현식(매크로가 잘 작동하는)을 작성 할 때(building the expression) 편한다. 다음의 인풋과 아웃풋을 보자. (어떻게 이게 동작하는지 잘보자.)
첫 매크로는 바로 머리에 때려박아주기 적절한 예시가 될것(ll give you enough to dive headfirst into your first macro.)
실제 클로저의 read 함수는 stream을 사용한다.(consume stream) 이런 행위는 셋업하는데 많은 짓을 해야 할지도 모른다.(verbose to set up.)
이 예제의 목적에 따라. read의 형제격인 read-string을 받겠다. (stream 대신 strings를 받는 것)
=================================================
(read-string "(+ 1 2 3 4 5)")
;==> (+ 1 2 3 4 5)
(class (read-string "(+ 1 2 3 4 5)"))
;=> clojure.lang.PersistentList
=================================================
여기 이 리스트가 우리가 지금까지 이야기 했던 데이터의 조각이다.
이건 클로저 리스트다. 그리고 아직까지는 클로저 코드다. (Code as Data)
readstring에서 돌아온 값은 평가될 준비가 되어있다.
이 리스트를 평가할 때, 그 표현식의 값을 알 수 있을 것이라 기대한다.
=================================================
(eval (read-string "(+ 1 2 3 4 5)"))
;=> 15
(class (eval (read-string "(+ 1 2 3 4 5)")))
;=> java.lang.Long
(+ 1 2 3 4 5)
;=> 15
=================================================
이게 REPL에서 실제로 일어나고 있는 일이다. 아니면 완전체 클로저(full-blown clojure project)를 실행할때에도 실제 일어난다. 코드는 자료구조(data structure)로 읽어지고(be read into, 저장되고) 그러고 나서 평가된다(eval). 이 두 스텝(read, evaluate)을 놓고 보았을 때,
read와 eval 사이에 코드자신이 평가되기 위해 자기자신을 끼워넣는 것(꼽사리로 들어가는 것)을 어렵지 않을 것이다.
예를들어 we could replace addition with multiplication:
=================================================
(let [expression (read-string "(+ 1 2 3 4 5)")
(cons (read-string "*") ;; 꼽사리로 들어가는 현장
(rest expression)))
;=> (* 1 2 3 4 5)
(eval *1) ;; *1 holds the result of the previous REPL evaluation
;=> 120
=================================================
이게 무슨 뜻이나면
=================================================
(cons
(read-string "*") ;; *
(rest expression))) ;; (rest 15)
; IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
; clojure.lang.RT.seqFrom (RT.java:505)
=================================================
(let [expression (quote (+ 1 2 3 4 5))]
(cons (quote *)
(rest expression)))
;=> (* 1 2 3 4 5)
(eval (let [expression (quote (+ 1 2 3 4 5))]
(cons (quote *)
(rest expression))))
;= 120
=================================================
quote verb는 사실 클로저의 특별한 형태(Clojure special form)이며 함수가 아니다. 우리는 이 verbs를 (공식적인 의미는 아님) 함수,매크로 그리고 special forms의 결합체라고 생각하면 된다.
리스트(Lists) 또한 verb 위치로 보여질 수 있는데 평가(eval)될 때는 함수위치로 축소되어야 한다.
함수(function)만이 아규먼트(여기서 아규먼트란 평가될 모~든 데이터를 말하는 듯 하다)를 제대로 평가한다. (컨트롤흐름을 코드(구현한 verbs)로 패스하기전에)
* Only functions uniformly evaluate their arguments before passing control to the code that implements the verb.
=================================================
(defn print-with-asterisks [printable-argument]
(print "*****")
(print printable-argument)
(println "*****"))
(print-with-asterisks "hi")
; *****hi*****
;= nil
=================================================
verb - 어떻게 정의하나? - 언제 아규먼트가 평가되나?
Functions - defn - Before executing the body
Macro -defmacro - Depends on macro; possibly multiple times or never.
Special form - We can’t! They’re only defined by the language. - Depends on special form; possibly multiple times or never.
자 위에 나온 print-with-asterisks같은 걸 만들 때는 굳이 언제 아규먼트가 평가(timing of the argument evaluation)되는지 알필요가 없다.
하지만 만약! 표현식을 아규먼트로 쓴다면! 그 해당 함수(print-with-asterisks)가 평가되기 전에 평가된다. (???)
=================================================
(print-with-asterisks
(do (println "in argument expression")
"hi"))
=================================================
잘보라 사용하기 전에 이미 김이 다 빠져나간거다.
Macros and speical forms, 는 이 룰에서 자유롭다. 그리고 어떤 순서에서 정의한 거든 아규먼트를 평가할 것이다. (아니면 이상하게 작동해서 망하던가...) 이게 함수와 매크로의 큰 차이란다.
quote expression (quote ...)안에 있는 코드는 평가되지 않는다. 그리고 아주 단순한 토큰들도 가능하다. (심볼, 리스트, 벡터.. 모두)
=================================================
(quote 1)
;= 1
(quote "hello")
;= "hello"
(quote :kthx)
;= :kthx
(quote kthx)
;= kthx
=================================================
근데 귀찮게 항상 QUOTE을써야 하나? 아니다 더 좋은게 있다.
Reader macro라는 것이다. 이건 우리가 만들 수 있는게 아니다. 언어딴에서 만들어진거라 한다.
=================================================
'(+ 1 2 3 4 5)
;= (+ 1 2 3 4 5)
'map
;= map
user.core=> map
#object[clojure.core$map 0x589a6fa "clojure.core$map@589a6fa"]
user.core= 'map
map
=================================================
오... 확실히 먼가 다르다.
=================================================
(let [expression '(+ 1 2 3 4 5)]
(cons '* (rest expression)))
;= (* 1 2 3 4 5)
=================================================
2016년 12월 22일 목요일
클로저로 단순한 GUI 프로그램을 만들어보았다.
그래서 하나를 짬내서 만들어 보았다. 처음에는 swing을 썼다. 참으로 고통이었다. seesaw라는 라이브러리가 있는 걸 확인하고 seesaw를 살짝 사용했다.
저녁에 짬내서 만들면서 느낀 점은 참으로 힘들었다는 점이다.
나의 능력이 문제인지, 내 뇌가 아직 클로저라는 리스프계열에 적응을 못하고 있는 것인지 잘 모르겠다.
막연히 Swing을 쓰면 되겠지라고 생각했었는데... 정말 자바로 그냥 짰으면 순식간에 만들었을 것이라는 생각이 들었다.
아직 내가 클로저를 짬내서 3개월 정도 해봤는데 어렵다는 것은 내가 아직 클로저가 가지는 개념을 모르고 있다는 생각이 든다.
하지만 확실한 것은 리스프 프로그래밍은 정말 어렵다. 처음으로 하는 GUI를 다루는 것이라 버튼 하나 넣는 것에도 애를 먹었다. 내가 고작 만든 것은 이메일만 필터링해서 나열하는 것이다.
대량메일을 보내기 위해서!
그래서 만약
AAA@AAA
BBB@BBB.com
CCC@CCC.net
뭐 이런게 있으면
BBB@BBB.com, CCC@CCC.net
뭐 이렇게 만들어 주는 것이다. 정말 별거 아닌 것을 만들었다. REPL에서 그냥 치면 바로 나오는 걸 만든 것이다.
그런데 참으로 느낀 점은...
재미있었다.
내 앞에 있는 무언가 보이지 않는 큰 힘이 꿈틀거리는데 잡을 수 없는... 그런 느낌?
잡으려고 마구 휘저었지만
잡지 못했고 별거아닌 노력의 산물만 남았지만
정말 재미있었다.
뭔가 쌓아올리는 느낌?
어쨋든 github로 관리하면서 더 손봐야겠다. (과연 할런지...)
https://github.com/ssisksl77/sman
2016년 12월 21일 수요일
[jvm]04 - Instruction Set Summary
Instruction Set Summary
JVM명령은 1바이트 연산코드(opcode)로 구성되어있다.뒤에 딸려오는 제로(zero) 또는 다른 피연산자(operands)는 연산에 쓰인다.많은 명령어들은 피연산자가 없고 그냥 연산코드만 가지고 있다.예외는 무시하고, JVM인터프리터의 내부 loop는
이렇게 생겼다고 한다. 피연산자의 수와 사이즈는 연산코드에 의해 결정된다. 만약 피연산자의 사이즈가 1바이트보다 크다면?
그것들은 빅엔디안(big-endian order : 데이터의 최상위 비트가 가장 높은 주소에 저장되므로 그냥 보기에는 역으로 보인다.)방식으로 저장된다.
예를들어, 로컬변수 unsigned 16비트 인덱스는 두개의 unsigned 바이트로 저장된다.(byte1 과 byte2).
값은
이다.
Types and the Java Virtual Machine
JVM 명령어 셋의 대부분은 명령의 타입정보를 인코드한다.예를들어 iload명령은 로컬 변수(int값이어야만 한다)의 내용을 피연산자 스택에 로딩한다.
fload는 iload와 똑같이 행동한다.(물론 float값을 사용한다.)
두 명령은 똑같은 구현을 가지고 있지만 서로 다른 연산부호(opcode)다.
- i : int operation
- l for long
- c for char
- f for float
- d for double
- a for reference
[jvm]03 Special Method
1. Special Method
자바로 만들어진 모든 생성자는 instance initialization method로 나타내어진다. (그놈의 이름은
클래스 또는 인터페이스는 최소한 한나의 초기화 메소드가 있다. 그리고 그 메소드를 호출해서 초기화가 진행된다.
그 초기화 메소드는 아규먼트가 없으며 void이다.
2. Exception
JVM에서 예외(exception) 단순히 Throwable, 아님 Throwable의 subclass의 클래스 인스턴스이다. 예외를 던지는 것은 예외가 던져진 곳(point)에서 즉각적 로컬 외(nonlocal) 컨트롤의 이전이 된다. 대부분의 예외는 동기적으로 일어난다. 비동기적인 예외 또한 프로그램 실쟁 중에 일어날 수도 있다.
2016년 12월 15일 목요일
[jvm]02 Run-Time Data Areas
JVM은 다양한 런타임 데이타 지역을 정의한다. 그리고 이것들은 프로그램 실행중이 사용된다. 몇몇 data area는 JVM이 시작할 때 만들어지고 JVM이 꺼질 때 파괴된다. 다른 data area는 thread 별로 있다. thread별 data area는 thread가 시작되고 죽을 때 그 삶을 같이 한다.
1. pc register
JVM은 다수의 스레드의 동시(at once) 실행을 지원한다. JVM의 스레드 각각은 자기 고유의 pc (program counter) register를 가진다. 어떤 시점에서든, 각각의 JVM스레드는 하나의 메소드 코드를 실행하고 있을 것이다. 즉 해당 스레드의 현재 메소드(current method)를 말이다. 만약 method가 native(native의 뜻은 뭐지? -> native method를 말하는 듯)하지 않다면, pc register는 현재 실행되고 있는 JVM명령어의 주소를 포함한다. 만약 메소드가 현재 스레드에 의해 실행되고 있는게 native라면, JVM의 pc register의 값은 undefined다. JVM의 pc register는 returnAddress 또는 native pointer(on specific platform)를 담기에 충분하다.
2. Java Virtual Machine Stacks
JVM Thread는 각각 private JVM stack을 가진다.(thread가 생성될 때 같이 만들어진다.) JVM stack은 frame을 저장한다.(frame은 나중에 나온다.) JVM stack은 C언어에서 만든거랑 유사하다. JVM stack은 push, pop frames를 제외하고 절대로 직접적으로 수정되지 않는다. frames는 힙에 할당되어진다. JVM stack을 위한 memory는 인접해 있을 필요없다.
3. heap
JVM은 JVM Thread가 공유하는 heap이라는 공간을 가지고 있다. heap은 모든 클래스 인스턴스와 배열이 할당되는 런타입 데이터 공간(run-time data area)이다. heap은 JVM이 시작될 때 만들어진다. 오브젝트를 위한 힙 저장소는 storage managerment system(가비지컬렉션)에 의해 되돌려 받는다. heap은 고정된 사이즈 또는 확장되도록 할 수도 있다.
4. Method Area
JVM이 공유하는 method area는 class의 구조 (such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface initialization.)를 담는다. method area는 JVM이 시작할 때 만들어진다. 비록 논리적으로 heap의 부분이다.
5. Run-Time Constant Pool
class파일 안에 per-class, per-interface의 런타임 constant_pool테이블을 보여주는 곳. (constant,ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time이 들어있음) The run-time constant pool은 일반적인 프로그래밍 언어에 심볼테이블(symbol table)과 유사한 것이라 생각하면 된다. 물론 이건 그것보다 더 많은 범위를 가지고 있다. 각각 run-time constant pool은 JVM의 method area에 할당되어 있다. class나 interface의 run-time constant는 걔네들이 만들어질때 JVM이 구성해준다.
5. Native Method Stacks
JVM은 일반적인 stack을 이용한다. 구어체로 "C tacks"라 부른다. 왜 stack을 사용하냐면 네이티브 메소드(native methods)를 지원하기 위해서다.(자바가 아닌 언어로 씌여진 메소드) 네이티브 메소드 스택은 JVM 명령어 모음의 인터프리터 구현(C언어로)으로 사용된다.
6. frames
프레임(frame)은 데이터와 부분 결과를 저장하려고 사용한다. 뿐만 아니라 dynamic linking, return values for methods, and dispatch exceptions. 이럴 때 사용된다. 새로운 프레임은 메소드가 호출될 때마다 각각 만들어진다. 그리고 메소드 호출이 완료되면 죽는다.(그것이 성공이건 익셉션이건...) 프레임은 각자 로컬변수 배열이 있고, 피연산자 스택 그리고 현재 메소드의 클래스의 런타임 constant pool의 참조.. 가 있다.
7. Local Variables
frame은 각각 local variables 배열을 가짐. 길이는 컴파일 타임에 결정 single local variable은 boolean, byte, char, short, int, float, reference, or returnAddress. A pair of local variables은 long or double. 타입을 가진다.
8. Operand Stacks
각각 frame은 last-in-first-out(LIFO)stack 을 가진다(known as its operand stack). 한가지 알아야 할 점은 double, long은 스택이 두개가 하나다. 그래서 스택에 쌓일 때 두개씩 쌓인다.
[jvm]01 Type and Values
1. Integral Types and Values The values
The values of the integral types of the Java Virtual Machine are:
- For byte, from -128 to 127 (-27 to 27 - 1), inclusive
- For short, from -32768 to 32767 (-215 to 215 - 1), inclusive
- For int, from -2147483648 to 2147483647 (-231 to 231 - 1), inclusive
- For long, from -9223372036854775808 to 9223372036854775807 (-263 to 263 - 1), inclusive
- For char, from 0 to 65535 inclusive
2. Floating-Point Types, Value Sets, and Values
부동소수점 타입(floating-point types)은 float과 double이 있다. 둘은 각각 개념적으로는 'IEEE 754 표준'에서 규정하는 32-bit single-precision과 64-bit double-precision 포맷이다.
IEEE 754 standard는 +/- 수만 포함하지 않고 +/- 0(제로값), +/-무한값, 그리고 Special Not-a-Number(NaN)값. NaN값은 0/0같은 유효하지 않은 연산의 결과를 표현할 때 쓰인다. 모든 JVM의 구현은 두개의 표준 집합을 지원하는 것이 요구된다. (float value set과 double value set)
returnAddress Type and Values
returnAddress type은 JVM의 jsr, ret 그리고 jsr_w 명령어에서 사용한다. returnAddress의 값은 JVM명령어들의 연산부호의 포인터들이다. 숫자 원시 타입(numeric primitive types)와 달리 returnAddress타입은 어느 자바 프로그래밍 언어 타입과 들어맞지 않고 running program에 수정될 수 없다.
3. boolean Type
비록 JVM이 불린타입을 정의한다 하지만, 아주 제한적인 것만을 지원한다. JVM에는 boolean 값을 위해 만들어진 명령어는 없다. 대신 JVM에 불린값을 int로 바꾸는 어구가 따로 있다. JVM은 boolean arrays는 직접적으로 지원한다. newarray 명령어는 불린타입배열를 만들 수 있다. 불린타입배열은 바이트배열(byte array)의 명령어로 접근되고 수정된다.(baload, bastore) *오라클 JVM의 구현에서는, 불린배열은 JVM 바이트배열로 인코딩 되어있다.(불린요소 하나당 8비트) JVM은 1,0을 인코딩해서 불린배열을 사용한다.(1=true, 0=false) 자바의 불린값이 int타입 값으로 매핑된다.(compiler가 함)
4.Reference Types and Values
reference type에는 3가지가 있다. : class type, array type, interface type 이것들의 값은 각각 동적으로 만들어지는 클래스 인스턴스, 배열 또는 인터페이스를 구현한 클래스 인스틴스 / 배열과 연결된다. 배열타입은 1차원 컴포넌트타입을 구성한다.(???) 배열타입의 컴포넌트 타입은 자기 스스로 배열 타입이다.(???) 만약, 어떤 배열타입에서 시작하면, 하나는 컴포넌트 타입을 고려하고, 그 후에 그 타입의 컴포넌트타입(이것도 배열이라면)을 고려한다. 계...속, 마침에(마지막에) 배열타입이 아닌 컴포넌트타입이 발견된다. 이걸 element type of an array type 이라고 한다. 배열의 요소타입이라고 하자. 이 요소타입은 원시 타입, 클래스 타입, 인터페이스 타입건 상관 없지면 이 이중에 있어야 한다. *여담: 컴포넌트타입은 확장이 가능하니 배열을 확장할 때 component type으로 확장되다가 값이 있는 element type으로 가면 거기서 멈추는 듯. 레퍼런스 값은 null reference를 가질 수도 있다. null reference는 오브젝트가 없음과 연결되어 있다. (null이란 말) null reference는 처음에는 run-time type을 가지지 않는다. 하지만 어떤 타입으로도 캐스팅 될 수 있다. 이 타입의 디폴트 값은 null이다. 이 명세는 구체적인 null 값을 강제하지 않는다.