% split -b 100m log_file log_
2013/12/25
linux meminfo 見方
cat /proc/meminfoとすることで、現在のメモリの使用状況を確認できる。
MemTotal: 1001008 kB
MemFree: 200708 kB
Buffers: 43400 kB
Cached: 395740 kB
SwapCached: 0 kB
Active: 557556 kB
Inactive: 178624 kB
HighTotal: 97216 kB
HighFree: 140 kB
LowTotal: 903792 kB
LowFree: 200568 kB
SwapTotal: 2096472 kB
SwapFree: 2096472 kB
Dirty: 224 kB
Writeback: 0 kB
Mapped: 356492 kB
Slab: 47820 kB
CommitLimit: 2596976 kB
Committed_AS: 689048 kB
PageTables: 6192 kB
VmallocTotal: 114680 kB
VmallocUsed: 4560 kB
VmallocChunk: 107264 kB
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB
★★★:
MemTotal メモリ搭載量
MemFree 空きメモリ量
Buffers Bufferのサイズ
Cached PageCacheのサイズ
SwapCached
PageoutされていたページをPageinして、まだディスクにPageoutした時のデータが残っているページの容量。空きメモリが足りなくなった場合は、Pageoutせずにそのまま解放できるのでI/Oを省略できる。
Active
最近アクセスされたページ。基本的に解放の対象外。
Inactive
最近アクセスのないページ。空きメモリが少なくなるとこのページから解放されていく。
HighTotal
HighMemoryの総容量
HighFree
HighMemoryの空き容量
LowTotal LowMemoryの総容量
LowFree LowMemoryの空き容量
SwapTotal スワップ領域の総容量
SwapFree スワップ領域の空き容量
Dirty Dirtyページのサイズ。ディスクへ書き出す必要がある。
Writeback スワップデバイスに書き出し中のページの容量
Mapped PageTableからマップされているページの容量
Slab スラブアロケータのメモリ使用量
CommitLimit プロセスが確保できるメモリの制限値(*4)
Committed_AS プロセスが割り当てているアドレス空間の総量(物理ページの割り当て量ではない)。malloc()だけしてまだアクセスしていない領域(仮想アドレス空間だけ割り当てられて物理ページが割り当てられていない状態)も含まれる。(VM的に'commit'されいつでも使用できる状態)
PageTables PageTableのメモリ使用量
VmallocTotal vmalloc()により確保するアドレス空間の総容量
VmallocUsed vmalloc()で割り当て済みの容量
VmallocChunk vmalloc用領域のFree領域で最も大きい連続領域のサイズ
★★★
カーネルが内部的に管理している枠組みでのメモリ情報をそのまま出しているので、残念ながらユーザ視点で知りたいメモリ情報とは一致しません。
MemTotal = MemFree + File-backedなメモリ + Anonymousなメモリ + カーネル空間が使うメモリ
★File-backedというのは、ディスクからメモリに読み込んだファイルなど、メモリを開放したくなったら、その内容をディスクに書き戻せば開放できるタイプのメモリです。
Active(file): 51452 kB
Inactive(file): 33560 kB
---------------------------------------------
| Buffers / Cached |
---------------------------------------------
| Active(file) / Inactive(file) | Shmem |
---------------------------------------------
Buffers + Cached = Active(file) + Inactive(file) + Shmem
Active(anon) + Inactive(anon) = Shmem + AnonPages
★Anonymousというのはそれ以外のメモリで、メモリを開放したくなったら、Swap領域に書き出さないと開放できないタイプのメモリ。
MemTotal: 1001008 kB
MemFree: 200708 kB
Buffers: 43400 kB
Cached: 395740 kB
SwapCached: 0 kB
Active: 557556 kB
Inactive: 178624 kB
HighTotal: 97216 kB
HighFree: 140 kB
LowTotal: 903792 kB
LowFree: 200568 kB
SwapTotal: 2096472 kB
SwapFree: 2096472 kB
Dirty: 224 kB
Writeback: 0 kB
Mapped: 356492 kB
Slab: 47820 kB
CommitLimit: 2596976 kB
Committed_AS: 689048 kB
PageTables: 6192 kB
VmallocTotal: 114680 kB
VmallocUsed: 4560 kB
VmallocChunk: 107264 kB
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB
★★★:
MemTotal メモリ搭載量
MemFree 空きメモリ量
Buffers Bufferのサイズ
Cached PageCacheのサイズ
SwapCached
PageoutされていたページをPageinして、まだディスクにPageoutした時のデータが残っているページの容量。空きメモリが足りなくなった場合は、Pageoutせずにそのまま解放できるのでI/Oを省略できる。
Active
最近アクセスされたページ。基本的に解放の対象外。
Inactive
最近アクセスのないページ。空きメモリが少なくなるとこのページから解放されていく。
HighTotal
HighMemoryの総容量
HighFree
HighMemoryの空き容量
LowTotal LowMemoryの総容量
LowFree LowMemoryの空き容量
SwapTotal スワップ領域の総容量
SwapFree スワップ領域の空き容量
Dirty Dirtyページのサイズ。ディスクへ書き出す必要がある。
Writeback スワップデバイスに書き出し中のページの容量
Mapped PageTableからマップされているページの容量
Slab スラブアロケータのメモリ使用量
CommitLimit プロセスが確保できるメモリの制限値(*4)
Committed_AS プロセスが割り当てているアドレス空間の総量(物理ページの割り当て量ではない)。malloc()だけしてまだアクセスしていない領域(仮想アドレス空間だけ割り当てられて物理ページが割り当てられていない状態)も含まれる。(VM的に'commit'されいつでも使用できる状態)
PageTables PageTableのメモリ使用量
VmallocTotal vmalloc()により確保するアドレス空間の総容量
VmallocUsed vmalloc()で割り当て済みの容量
VmallocChunk vmalloc用領域のFree領域で最も大きい連続領域のサイズ
★★★
カーネルが内部的に管理している枠組みでのメモリ情報をそのまま出しているので、残念ながらユーザ視点で知りたいメモリ情報とは一致しません。
MemTotal = MemFree + File-backedなメモリ + Anonymousなメモリ + カーネル空間が使うメモリ
★File-backedというのは、ディスクからメモリに読み込んだファイルなど、メモリを開放したくなったら、その内容をディスクに書き戻せば開放できるタイプのメモリです。
Active(file): 51452 kB
Inactive(file): 33560 kB
---------------------------------------------
| Buffers / Cached |
---------------------------------------------
| Active(file) / Inactive(file) | Shmem |
---------------------------------------------
Buffers + Cached = Active(file) + Inactive(file) + Shmem
Active(anon) + Inactive(anon) = Shmem + AnonPages
★Anonymousというのはそれ以外のメモリで、メモリを開放したくなったら、Swap領域に書き出さないと開放できないタイプのメモリ。
2013/12/19
jpa GenerationType
GenerationType.TABLE
テーブルを使います。
GenerationType.SEQUENCE
シーケンスを使います。
GenerationType.IDENTITY
データベース固有の識別子自動生成を使います。
GenerationType.AUTO (デフォルト)
データベースに応じて TABLE・SEQUENCE・IDENTITY のいずれかが選択されます。 IDENTITY が使える場合は IDENTITY に、 IDENTITY が使えなくて SEQUENCE が使える場合は SEQUENCE に、 IDENTITY も SEQUENCE も使えない場合は TABLE になります。
テーブルを使います。
GenerationType.SEQUENCE
シーケンスを使います。
GenerationType.IDENTITY
データベース固有の識別子自動生成を使います。
GenerationType.AUTO (デフォルト)
データベースに応じて TABLE・SEQUENCE・IDENTITY のいずれかが選択されます。 IDENTITY が使える場合は IDENTITY に、 IDENTITY が使えなくて SEQUENCE が使える場合は SEQUENCE に、 IDENTITY も SEQUENCE も使えない場合は TABLE になります。
2013/12/19
JPA entity status など
New (transient): an entity is new if it has just been instantiated using the new operator, and it is not associated with a persistence context. It has no persistent representation in the database and no identifier value has been assigned.
Managed (persistent): a managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.
Detached: the entity instance is an instance with a persistent identity that is no longer associated with a persistence context, usually because the persistence context was closed or the instance was evicted from the context.
Removed: a removed entity instance is an instance with a persistent identity, associated with a persistence context, but scheduled for removal from the database.
The EntityManager API allows you to change the state of an entity, or in other words, to load and store objects. You will find persistence with JPA easier to understand if you think about object state management, not managing of SQL statements.
★javaX EitheManager persist merge
Either way will add an entity to a PersistenceContext, the difference is in what you do with the entity afterwards.
Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates to the entity will be tracked)
Merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again).
SomeClass javax.persistence.EntityManager.merge(SomeClass arg0)
==>you can get the object after insert
Maybe a code example will help.
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database (you made the changes *after* merging
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated (the changes were made to e2, not e)
★Detaching a object
An object when loaded in the persistence context is managed by Hibernate. You can force an object to be detached (ie. no longer managed by Hibernate) by closing the EntityManager or in a more fine-grained approach by calling the detach() method.
Cat cat = em.find( Cat.class, new Long(69) );
...
em.detach(cat);
cat.setName("New name"); //not propatated to the database
★SomeObject object=new SomeObject();
object.setId(1);
object.setColumn1("");
entityManager.persist();
----->エラーになった。。detached entity passed to persist
This is because Hibernate thinks that this object was returned from the Entity Manager since it has a primary key assigned. One way to solve this is by using a merge() function. This is the default way that Spring Roo does it.
★Hibernate: “Field 'id' doesn't have a default value”
ーー>SOLVED: recreating the database solved the problem
---->何?説明しろ
If you want MySQL to automatically produce primary keys then you have to tell it when creating the table. You don't have to do this in Oracle.
ーーー>たしかに"AUTO_INCREMENT "されていないね。。。。。。
Managed (persistent): a managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.
Detached: the entity instance is an instance with a persistent identity that is no longer associated with a persistence context, usually because the persistence context was closed or the instance was evicted from the context.
Removed: a removed entity instance is an instance with a persistent identity, associated with a persistence context, but scheduled for removal from the database.
The EntityManager API allows you to change the state of an entity, or in other words, to load and store objects. You will find persistence with JPA easier to understand if you think about object state management, not managing of SQL statements.
★javaX EitheManager persist merge
Either way will add an entity to a PersistenceContext, the difference is in what you do with the entity afterwards.
Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates to the entity will be tracked)
Merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again).
SomeClass javax.persistence.EntityManager.merge(SomeClass arg0)
==>you can get the object after insert
Maybe a code example will help.
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database (you made the changes *after* merging
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated (the changes were made to e2, not e)
★Detaching a object
An object when loaded in the persistence context is managed by Hibernate. You can force an object to be detached (ie. no longer managed by Hibernate) by closing the EntityManager or in a more fine-grained approach by calling the detach() method.
Cat cat = em.find( Cat.class, new Long(69) );
...
em.detach(cat);
cat.setName("New name"); //not propatated to the database
★SomeObject object=new SomeObject();
object.setId(1);
object.setColumn1("");
entityManager.persist();
----->エラーになった。。detached entity passed to persist
This is because Hibernate thinks that this object was returned from the Entity Manager since it has a primary key assigned. One way to solve this is by using a merge() function. This is the default way that Spring Roo does it.
★Hibernate: “Field 'id' doesn't have a default value”
ーー>SOLVED: recreating the database solved the problem
---->何?説明しろ
If you want MySQL to automatically produce primary keys then you have to tell it when creating the table. You don't have to do this in Oracle.
ーーー>たしかに"AUTO_INCREMENT "されていないね。。。。。。
2013/12/19
ビッグデータ MapReduce
ビッグデータは、高ボリューム、高速度、高バラエティの情報資産のいずれか(あるいは全て)であり、新しい形の処理を必要とし、意思決定の高度化、見識の発見、プロセスの最適化に寄与する
MapReduce
ーーーーーーーーーーーー
map関数は、配列要素に1つずつ順番に処理を加えていき、
配列として結果が返ってきます
ーーーーーーーーーーーー
reduce関数-->配列の(左から右へ) 2 つの値に対して同時に関数を適用し、単一の値にします
★★javaScript
var ary = [1,2,3,4,5];
function sum(a) {
return a.reduce(function(x, y) { return x + y; });
}
// 初回
// x = 1, y = 2
// 累積値: 3
// 2回目
// x = 3(累積値), y = 3
// 累積値: 6
。。。
ーーーーーーーーーーーー
Map ステップ - マスターノードは、入力データを受け取り、それをより細かい単位に分割し、複数のワーカーノードに配置する。受け取ったワーカーノードが、更に細かい単位に分割し、他の複数のワーカーノードに配置するという、より深い階層構造の分割を行うこともある。そして、各ワーカーノードは、その細かい単位のデータを処理し、処理結果を、マスターノードへと返す。
Reduce ステップ - 続いて、マスターノードが、Mapステップでの処理結果を集約し、目的としていた問題に対する答え(結果)を何らかの方法によって出力する。
ビッグデータの処理においては、並列分散処理が有効になるが、これは人工知能の研究から生まれた仕組みである。人間の脳は、約1200億のニューロンが並列処理をしており、情報の伝達速度は時速350~400キロといわれている。1つのニューロンが死んでしまっても、処理が停止しない頑強性も備えている。こうした仕組みに基づいて、並列分散処理やMPP、Hadoopなどが開発されている
インフラに投資する場合、まず必要なのはデータであり、次が問い合わせ。まず蓄積して、次に構造化である"と語っている。工藤氏は、「時と場合にもよるが、データの利用方法は刻々と変化するので、構造化を考えすぎて機会を逃すのは本末転倒。データを蓄積しておいて構造化や問い合わせを考えることに関して異論はない
分散処理をどのように実行するかも気になりますが、
MapReduceアルゴリズムを利用する場合は、
まずは「自分が実行させたい処理をどのように実現すればいいのか」という点から理解していくのが早道です。
ここでは分散処理そのものではなく、
分散処理されるプログラムがどういうものかを理解することに主眼を置くことにします。
入力データを分割して「Map処理」を行うプログラムで分散処理をさせ、
その結果を「Reduce処理」を行うプログラムへ渡して、そちらも分散処理をしています。
分散処理を行うプログラムを開始したり停止したりする制御やデータ入出力の同期といったことは「Masterプログラム」が行っています
文字カウンター abcaba
map: a b c a b a
1 1 1 1 1 1
sort:
a a a b b c
1 1 1 1 1 1
reduce:
a
a a a
1 1 a
b
b b
1 1
c
c
1
=>
a b c
3 2 1
Keep it short and simple!!
MapReduce
ーーーーーーーーーーーー
map関数は、配列要素に1つずつ順番に処理を加えていき、
配列として結果が返ってきます
ーーーーーーーーーーーー
reduce関数-->配列の(左から右へ) 2 つの値に対して同時に関数を適用し、単一の値にします
★★javaScript
var ary = [1,2,3,4,5];
function sum(a) {
return a.reduce(function(x, y) { return x + y; });
}
// 初回
// x = 1, y = 2
// 累積値: 3
// 2回目
// x = 3(累積値), y = 3
// 累積値: 6
。。。
ーーーーーーーーーーーー
Map ステップ - マスターノードは、入力データを受け取り、それをより細かい単位に分割し、複数のワーカーノードに配置する。受け取ったワーカーノードが、更に細かい単位に分割し、他の複数のワーカーノードに配置するという、より深い階層構造の分割を行うこともある。そして、各ワーカーノードは、その細かい単位のデータを処理し、処理結果を、マスターノードへと返す。
Reduce ステップ - 続いて、マスターノードが、Mapステップでの処理結果を集約し、目的としていた問題に対する答え(結果)を何らかの方法によって出力する。
ビッグデータの処理においては、並列分散処理が有効になるが、これは人工知能の研究から生まれた仕組みである。人間の脳は、約1200億のニューロンが並列処理をしており、情報の伝達速度は時速350~400キロといわれている。1つのニューロンが死んでしまっても、処理が停止しない頑強性も備えている。こうした仕組みに基づいて、並列分散処理やMPP、Hadoopなどが開発されている
インフラに投資する場合、まず必要なのはデータであり、次が問い合わせ。まず蓄積して、次に構造化である"と語っている。工藤氏は、「時と場合にもよるが、データの利用方法は刻々と変化するので、構造化を考えすぎて機会を逃すのは本末転倒。データを蓄積しておいて構造化や問い合わせを考えることに関して異論はない
分散処理をどのように実行するかも気になりますが、
MapReduceアルゴリズムを利用する場合は、
まずは「自分が実行させたい処理をどのように実現すればいいのか」という点から理解していくのが早道です。
ここでは分散処理そのものではなく、
分散処理されるプログラムがどういうものかを理解することに主眼を置くことにします。
入力データを分割して「Map処理」を行うプログラムで分散処理をさせ、
その結果を「Reduce処理」を行うプログラムへ渡して、そちらも分散処理をしています。
分散処理を行うプログラムを開始したり停止したりする制御やデータ入出力の同期といったことは「Masterプログラム」が行っています
文字カウンター abcaba
map: a b c a b a
1 1 1 1 1 1
sort:
a a a b b c
1 1 1 1 1 1
reduce:
a
a a a
1 1 a
b
b b
1 1
c
c
1
=>
a b c
3 2 1
Keep it short and simple!!
2013/12/16
java System.outの出力先をファイルにする
catalina.outを生成するには、どうしたらいいの?
System.outおよびSystem.errは、ともにcatalina.outへ書き出されます。 しかしながら、swallowOutput属性経由でこれを抑制し、異なるログファイルに送出することもできます。
そうすることで、catalina.outは生成されなくなります。 しかし、そのことが問題にならないわけではありません。 というのも、ロギングパッケージを使用するようになってから、標準出力へ書き出されなくなりましたよね?
詳しいことは log4jの設定例 および catalina.outの生成に関するスレッド を参照してください。
File outFile = new File("/var/log/jiang/stdout.log");
PrintStream outStream = new PrintStream(new FileOutputStream(outFile));
System.setOut(outStream);
File errFile = new File("/var/log/jiang/stderr.log");
PrintStream errStream = new PrintStream(new FileOutputStream(errFile));
System.setErr(errStream);
System.outおよびSystem.errは、ともにcatalina.outへ書き出されます。 しかしながら、swallowOutput属性経由でこれを抑制し、異なるログファイルに送出することもできます。
そうすることで、catalina.outは生成されなくなります。 しかし、そのことが問題にならないわけではありません。 というのも、ロギングパッケージを使用するようになってから、標準出力へ書き出されなくなりましたよね?
詳しいことは log4jの設定例 および catalina.outの生成に関するスレッド を参照してください。
File outFile = new File("/var/log/jiang/stdout.log");
PrintStream outStream = new PrintStream(new FileOutputStream(outFile));
System.setOut(outStream);
File errFile = new File("/var/log/jiang/stderr.log");
PrintStream errStream = new PrintStream(new FileOutputStream(errFile));
System.setErr(errStream);
mysql update insert 同時 重複など hibernate native query
★REPLACE
INSERT文と同じ構文でINSERTの部分をREPLACEに置き換えたもの
動作としては通常のINSERTと同じでDUPLICATE KEYが発生した場合に該当のレコードを削除してINSERT
★INSERT ON DUPLICATE KEY UPDATE
INSERT文のオプションとして記述
動作としては通常のINSERTと同じでDUPLICATE KEYが発生した場合にオプションで指定したUPDATE文を実行
・INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
・INSERT INTO table (a,b,c) VALUES select a,b,c where ***
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
★insert ignore ....重複データがあれば、無視される
INSERT文と同じ構文でINSERTの部分をREPLACEに置き換えたもの
動作としては通常のINSERTと同じでDUPLICATE KEYが発生した場合に該当のレコードを削除してINSERT
★INSERT ON DUPLICATE KEY UPDATE
INSERT文のオプションとして記述
動作としては通常のINSERTと同じでDUPLICATE KEYが発生した場合にオプションで指定したUPDATE文を実行
・INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
・INSERT INTO table (a,b,c) VALUES select a,b,c where ***
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
★insert ignore ....重複データがあれば、無視される
mysql show prcesslist full 条件付け
SHOW FULL PROCESSLIST;
SELECT * FROM information_schema.PROCESSLIST where info like '%select%';
describe information_schema.processlist;
ID bigint(4) NO 0
USER varchar(16) NO
HOST varchar(64) NO
DB varchar(64) YES
COMMAND varchar(16) NO
TIME int(7) NO 0
STATE varchar(64) YES
INFO longtext YES
SELECT * FROM information_schema.PROCESSLIST where info like '%select%';
describe information_schema.processlist;
ID bigint(4) NO 0
USER varchar(16) NO
HOST varchar(64) NO
DB varchar(64) YES
COMMAND varchar(16) NO
TIME int(7) NO 0
STATE varchar(64) YES
INFO longtext YES
java tomat classLoader
★★Javaはクラスが最初に参照された際,その時点で使用しているクラスローダーを使用して,クラスパスからクラスファイルをロードし,その参照をクラスローダー自身に保持します.
クラスローダーの細かい仕組みを触る
・JVM起動の際,java.*などの標準のクラス群をロードするクラスローダー
・JVMが起動後,mainメソッドを保持するアプリケーションプログラムをロードするクラスローダー
・その他,自分で勝手に作成したクラスローダー
最初のやつは「ブートストラップクラスローダー」で,次のが「システムクラスローダー」で,最後のヤツが単なるアプリケーションのクラスローダーです.
クラスローダーには親子関係があり,ブートストラップクラスローダーが一番の親,その子がシステムクラスローダー,我々が独自に作成するクラスローダーは,システムクラスローダーの子となります.もちろん,我々が独自に作成したクラスローダー間にも,親子関係を持たせることもできます.
ブートストラップクラスローダーは,JAVA_HOME/lib/*.jarなどに対してのクラスパスを保持しています.
システムクラスローダーは,javaコマンドの引数(または環境変数CLASSPATH)によって指定されたクラスパスを保持しています.
クラスがロードされる際,親から末端の子クラスローダーへ,順番にロード処理が行われます.
親クラスローダーが,既にクラスの参照を保持している場合,それが使用され,クラスファイルはロードされません.
何が言いたかったのかといいますと,JVM起動中にロード/アンロードしたいクラスは,アプリケーション起動時のクラスパスに含めてはならない,ということです.
アプリケーション起動時のクラスパスに含めるということは,システムクラスローダーがそれらをロードし,そして変更できなくなってしまうからです(JVM起動中にシステムクラスローダーを破棄することはできません).
★★
クラスローダーの細かい仕組みを触る
・JVM起動の際,java.*などの標準のクラス群をロードするクラスローダー
・JVMが起動後,mainメソッドを保持するアプリケーションプログラムをロードするクラスローダー
・その他,自分で勝手に作成したクラスローダー
最初のやつは「ブートストラップクラスローダー」で,次のが「システムクラスローダー」で,最後のヤツが単なるアプリケーションのクラスローダーです.
クラスローダーには親子関係があり,ブートストラップクラスローダーが一番の親,その子がシステムクラスローダー,我々が独自に作成するクラスローダーは,システムクラスローダーの子となります.もちろん,我々が独自に作成したクラスローダー間にも,親子関係を持たせることもできます.
ブートストラップクラスローダーは,JAVA_HOME/lib/*.jarなどに対してのクラスパスを保持しています.
システムクラスローダーは,javaコマンドの引数(または環境変数CLASSPATH)によって指定されたクラスパスを保持しています.
クラスがロードされる際,親から末端の子クラスローダーへ,順番にロード処理が行われます.
親クラスローダーが,既にクラスの参照を保持している場合,それが使用され,クラスファイルはロードされません.
何が言いたかったのかといいますと,JVM起動中にロード/アンロードしたいクラスは,アプリケーション起動時のクラスパスに含めてはならない,ということです.
アプリケーション起動時のクラスパスに含めるということは,システムクラスローダーがそれらをロードし,そして変更できなくなってしまうからです(JVM起動中にシステムクラスローダーを破棄することはできません).
★★
l Tomcat的container中需要一个自定义的loader,不能简单的直接使用系统的loader,因为所要运行的servlet是不可信任的。假如像使用之前章节一样使用系统中的loader,则该loader载入的servlet和其他类就能访问当前jvm实例中CLASSPATH环境变量下所有的类了,这非常不安全。servlet应该仅仅被允许从WEB-INF/classes及其子目录,和WEB-INF/lib下部署额lib中载入类。因此,servlet容器需要一个自定义的loader。每一个web一样(对应context容器)都有一个自定义的loader。在catalina中,loader组件要实现org.apache.catalina.Loader接口。
l 使用自定义loader的另一个原因是,可以支持class的自动重载。tomcat中的loader实现使用了另一个线程来检查servlet和相关类的时间戳,若是发生变化,则重新载入。为了支持class的重载,loader还要实现org.apache.catalina.loader.Reloader接口
l tomcat 中使用自定义类载入器的原因如下: (1)指定明确的类载入规则 (2)缓存已经载入的类 (3)预载入某个类,更方便使用。
l jvm使用三种loader:引导类载入器(bootstrap
class loader),扩展类载入器(extension class loader)和系统类载入器(system class
loader)。这三种有着父子继承关系(引导类载入位于最高层)。 引导类载入器(bootstrap
class loader)用于引导jvm。当使用java命令时,引导类载入器开始工作。引导类载入是使用本地方法实现的,因为它要负责载入启动jvm的类。此外,它还要负责载入java核心类,例如java.io和java.lang包下的类,它的搜索路径包括rt.jar和i18n.jar等包,具体查找哪些包依赖于jvm和操作系统的版本。
扩展类载入器(extension class loader)负责载入标准扩展目录下的类。这有利与程序开发,因为程序员只需要将jar包拷贝到扩展目录中,扩展类载入器会从这些jar包中查找需要的类。扩展目录依赖于jvm的具体实现。sun的jvm实现中标准扩展目录是“/jdk/jre/lib/ext”。 系统类载入器(system class loader)是默认的类载入器,从CLASSPATH中搜索需要的类。 那么,jvm到底使用哪个loader呢?为了尽可能的安全,jvm在这个问题上使用代理模型。每次需要载入一个新类时,首先使用系统类载入器。但是,该类并不会被立刻载入。相反,系统类载入器将这个任务代理给扩展类载入器执行,而扩展类载入器又将该任务代理给引导类载入器执行。因此,引导类载入器实际上是先载入这个类的。当引导类载入器无法载入这个类时,扩展类载入器尝试载入这个类,若扩展类载入器也载入失败,则系统类载入器尝试载入,若还是失败,抛出java.lang.ClassNotFoundException异常。