[小西ホームページ]   [目次・索引]   [前の授業]

情報処理IIIA(Javaプログラミング入門)第13回

目次 索引
13.1 より高度な話題
13.1.1 バイトコードと仮想マシン
13.1.2 アプレット・セキュリティ
13.1.3 ガベージ・コレクション
13.2 成績評価について
13.3 センターのアンケート
Java仮想マシン  Javaバイトコード  インタプリタ  オブジェクトコード  ガベージ・コレクション  機械語  原始プログラム  高級言語  コンパイラ  ソースコード  ヒープ  目的プログラム 

13.1 より高度な話題

13.1.1 バイトコードと仮想マシン

これまで、色々なJavaプログラムを作成し、実行してきました。 実は、コンピュータはJavaプログラムを理解しません。 コンピュータが直接Javaプログラムを実行しているわけではないのです。 ここでは、Javaプログラムが実行される仕組みについて説明します。

まず、コンピュータが直接理解できるのは、機械語だけです。 機械語machine language ) とは、コンピュータを直接制御するプログラミング言語です。 Javaプログラムが構造を持った英単語などの列なのに対して、機械語のプログラムは構造のないビットの列です。 また、同じ制御を行う場合でも、コンピュータ・システムが異なりますと、異なるビット列になります。 機械語によるプログラミングは、システムごとに異なるビット列を、構造不明のまま書く、困難な作業なのです。

プログラミングをもう少し楽にするために、高級言語が考え出されました。 高級言語high level language ) とは、コンピュータにやってほしいことを、人間にとって分かりやすく書けるように作られたプログラミング言語です。 高級言語はプログラムの構造を反映する文法を持ち、英単語など覚えやすい構成要素から成り立ちます。 Javaは高級言語の一つです。 Javaの他にも、CやBASICなど数多くの高級言語が使われています。 もちろん、高級言語で書いたプログラムをコンピュータに与えても、機械語でないので実行できません。

実行するために、コンパイラやインタプリタを利用します。 コンパイラcompiler ) とは、高級言語で書かれたプログラムを機械語のプログラムに変換するソフトウェアです。 高級言語の中では、例えばC言語が通常コンパイラを使います。 C言語でプログラムを書き、Cコンパイラによってそれを機械語に変換したあとで、コンピュータにその機械語プログラムを実行させるのです。 コンピュータ・システムが異なれば機械語も異なりますので、システムごとにCコンパイラを用意する必要がありますが、Cプログラム自身はシステムの差をあまり意識せずに書けるのです。 なお、変換前のプログラムは ソースコードsource code ) 、または 原始プログラムsource program ) とよばれます。 また、変換後のプログラムは オブジェクトコードobject code ) 、または 目的プログラムtarget program ) とよばれます。

一方、 インタプリタinterpreter ) とは、高級言語で書かれたプログラムを少しずつ読みながら、その部分に対応する機械語プログラムを探して実行していくソフトウェアです。 高級言語の中では、例えばBASICが通常インタプリタを使います。 BASICでプログラムを書き、BASICインタプリタを実行しますと、プログラムの内容を少し読んでは機械語プログラムを見つけて実行ということを繰り返します。 コンパイラの場合と同様に、システムごとにBASICインタプリタを用意する必要がありますが、BASICプログラム自身はシステムの差をあまり意識しなくてもよいのです。

さて、Javaプログラムは、コンパイラ・インタプリタ方式で実行されます。 コンパイラ・インタプリタ方式とは、さまざまなコンピュータ・システムの共通語のような機械語を定め、コンパイラによって原始プログラムを共通機械語のプログラムに変換し、その後で共通機械語インタプリタを実行するという方式です。 共通機械語は、実在する必要のないコンピュータ・システムの機械語と考えられます。 そこで、仮想マシン(または仮想計算機)という概念を導入し、共通機械語は仮想マシンの機械語であると位置付けることにします。 Javaのための共通機械語は、 JavaバイトコードJava Bytecode ) です。 Javaのための仮想マシンは、 Java仮想マシンJava virtual machine ) とよばれます。

ここで、Javaアプリケーションを実行するときに、javacコマンドとjavaコマンドを用いたことを思い出してください。 javacコマンドは、Javaプログラム(.java)をバイトコード(.class)に変換するコンパイラです。 そして、javaコマンドはバイトコードインタプリタなのです。 一方、Javaアプレットを実行するときは、javacコマンドでバイトコードを生成し、それをアプレット・ビューアやWWWブラウザに読み込ませました。 アプレット・ビューアやWWWブラウザには、バイトコードインタプリタの機能が備わっていて、それが働いているのです。 アプレット・ビューアやWWWブラウザの中には仮想マシンがあると言うこともできます。

13.1.2 アプレット・セキュリティ

この授業では、アプレットを使ってお絵描きをしました。 本当は、アプレットはもっと色々なことができます。 楽しいアプレットが埋め込まれたホームページは非常に魅力的です。

ここで、アプレットもれっきとしたプログラムであること、そしてそのホームページを訪れると、アプレットが転送され、あなたのコンピュータの上で動くことを思い出してください。 悪意のある人の作ったアプレットが、あなたのコンピュータのファイルを消したり、個人情報を盗んだりしないのでしょうか。

この心配への答えは、「アプレットにできることは制限されていて、悪事を働くのは困難である。」です。 以下では、個人情報(具体的にはユーザ名)がアプレットで取り出せないことを確かめます。

はじめに、スタンドアロン・アプリケーションで、Javaシステムに関する情報(システム・プロパティとよばれます)を取り出します。 次のプログラムは、Javaシステムが動いているOSの名前を表示します。

/* 1*/ class PropertyTest1 {
/* 2*/     public static void main (String[] args) {
/* 3*/         String s = System.getProperty("os.name");
/* 4*/         System.out.println(s);
/* 5*/     }
/* 6*/ }
b00a001@Ampere:~/java% java PropertyTest1
Solaris
b00a001@Ampere:~/java%

3行目の System クラスのクラスメソッド getProperty がシステム・プロパティを取り出します。 引数はシステム・プロパティの名前(文字列)です。 OSの名前はos.nameで、ユーザ名はuser.nameです。 (システム・プロパティは他にもあります。) したがって、3行目の "os.name""user.name" に置き換えますと、ユーザ名(この場合はb00a001)が表示されます。

次に、同じことをアプレットで行ないます。 7行目の Graphics クラスのインスタンスメソッド drawString はアプレットで文字列を描画するものです。 この第2引数と第3引数は、文字列イメージの左下の座標です。

PropertyTest2.java
/* 1*/ import java.applet.*;
/* 2*/ import java.awt.*;
/* 3*/
/* 4*/ public class PropertyTest2 extends Applet {
/* 5*/     public void paint (Graphics g) {
/* 6*/         String s = System.getProperty("os.name");
/* 7*/         g.drawString(s, 0, 20);
/* 8*/     }
/* 9*/ }
PropertyTest2.html
<applet code="PropertyTest2.class" width="200" height="100">
</applet>
b00a001@Ampere:~/java% cd
b00a001@Ampere:~% appletviewer java/PropertyTest2.html &
b00a001@Ampere:~%
Applet Viewer
Applet
Solaris
applet started
図 13.1  OSの名前を描画するアプレット

さて、 "os.name""user.name" に置き換えて、スタンドアロン・アプリケーションのときのようにユーザ名が取り出せるでしょうか。 試してみますと、アプレット・ビューアは起動しますが、ユーザ名は描画されません。 その代わり、端末エミュレータにエラーメッセージが出力されます。 セキュリティ上の問題があるのでアプレットを停止するということです。

b00a001@Ampere:~% appletviewer java/PropertyTest2.html &
*** Security Exception: properties ***
sun.applet.AppletSecurityException: security.properties
    (中略)
b00a001@Ampere:~%

注意: 設定によっては、アプレット・ビューアでユーザ名が描画されてしまいます。 (環境変数 CLASSPATH が関係します。) この場合は、WWWブラウザでアプレットを表示してください。

13.1.3 ガベージ・コレクション

Javaは高い信頼性を持つ言語です。 この信頼性を支える仕組みの一つに、 ガベージ・コレクションgarbage collection ) があります。

コンピュータのメモリ領域は有限ですので、大規模なデータ処理を行なう際には、不要なデータを破棄し、メモリを開放する必要が生じます。 多くのプログラミング言語では、この作業はプログラマの責任で行ないます。 しかし、不要なデータをいつまでも残してメモリ不足を引き起こしたり、誤った開放によって必要なデータを失ったりというバグを導きやすいのも事実です。

Javaは、この面倒な作業を自動的に行ないます。 これがガベージ・コレクションです。 ガベージ・コレクションでは、誰からも参照されないデータを不要なものと見なし、機械的にそれを検知してメモリを開放します。 機械的な部分で多少時間がかかりますが、バグの要因が減った分、信頼性の高いプログラムが書けるのです。

ガベージ・コレクションの働く例を以下に示します。 ここで ヒープheap ) とは、プログラムの実行中に確保されるメモリ領域のことです。

次のプログラムは、単にメモリを消費するものです。 Java言語では、 int 型のデータは4バイトと決まっていますので、2次元配列 a の消費するメモリは8×65536×4バイト、すなわち2MBです。

/* 1*/ class GCTest1 {
/* 2*/     public static void main (String[] args) {
/* 3*/         int i;
/* 4*/         int[][] a = new int[8][];
/* 5*/         for (i = 0; i < 8; i++) {
/* 6*/             a[i] = new int[65536];
/* 7*/             System.out.println(i);
/* 8*/         }
/* 9*/     }
/*10*/ }

Javaインタプリタは、デフォルトでは十分な(16MB)最大ヒープ領域を持ちますので、このプログラムは問題なく実行できます。

b00a001@Ampere:~/java% java GCTest1
0
1
2
3
4
5
6
7
b00a001@Ampere:~/java%

そこで、ヒープ領域を1MBに制限してみます。 オプションの ms は初期ヒープ領域、 mx は最大ヒープ領域を表します。 1MBのヒープ領域に2MBのデータは収まりませんので、プログラムは途中でエラーを起こして停止します。

b00a001@Ampere:~/java% java -ms1m -mx1m GCTest1
0
1
2
java.lang.OutOfMemoryError
        at GCTest1.main(GCTest1.java:6)
b00a001@Ampere:~/java%

次に、以下のようなプログラムを考えます。 これは上記のプログラムに似ています。 違いは、あらかじめ5行目で配列 b を用意しておき、それを8行目で a[i] に格納しなおすところです。

/* 1*/ class GCTest2 {
/* 2*/     public static void main (String[] args) {
/* 3*/         int i;
/* 4*/         int[][] a = new int[8][];
/* 5*/         int[] b = new int[65536];
/* 6*/         for (i = 0; i < 8; i++) {
/* 7*/             a[i] = new int[65536];
/* 8*/             a[i] = b;
/* 9*/             System.out.println(i);
/*10*/         }
/*11*/     }
/*12*/ }

配列 b がありますので、プログラムGCTest2はGCTest1よりメモリを消費するように思えます。 しかし、GCTest2は1MBのヒープ領域で実行できるのです。

b00a001@Ampere:~/java% java -ms1m -mx1m GCTest2
0
1
2
3
4
5
6
7
b00a001@Ampere:~/java%

この現象を理解するには、GCTest2の7行目と8行目に注目する必要があります。 7行目では、確かに65536要素の配列を生成し、それを a[i] に格納しています。 しかし、8行目で a[i] に配列 b を格納しなおしますので、7行目で生成した配列は誰も参照できなくなります。 誰も参照できないデータは記憶しておく必要がありませんので、Javaインタプリタはその領域を自動的に検知し、他のデータの記憶のために再利用するのです。


13.2 成績評価について

レポート(アンケートも含む)の提出に関するスケジュールは以下の通りです。

1月20日(月)
全員にレポートの提出状況をメールで知らせます。
1月31日(金)
レポートの最終締め切り日です。 これ以降提出されたレポートは採点しません。

この授業の成績は、レポートの得点とアンケートの提出(出席点)で決まります。

レポート(11通)
A=8点、B=7点、C=6点、D=5点、Fおよび未提出=0点。
アンケート(9/26, 1/9)
提出=6点、未提出=0点。

成績に関して以下のような事情のある人はメールで連絡してください。 できる限り対処します。


13.3 センターのアンケート

情報処理センターのアンケートに答えてください。


[小西ホームページ]   [目次・索引]   [前の授業]

2003年1月9日更新
konishi@twcu.ac.jp
Copyright (C) 2003 Zenjiro Konishi. All rights reserved.