kagamihogeの日記

kagamihogeの日記です。

Why String Class is made Immutable or Final in Java - 5 Reasons をテキトーに訳した

http://java67.blogspot.sg/2014/01/why-string-class-has-made-immutable-or-final-java.html をテキトーに訳した。

Why String Class is made Immutable or Final in Java - 5 Reasons

Stringについて聞かれないJavaの採用面接はほとんど無く*1、なぜJavaのStringはイミュータブルなのか?(Why String is Immutable in Java)はその最たるものと言えます。なぜJavaのStringはイミュータブルなのか?(Why String is Immutable in Java)は、言い換えると、なぜJavaのStringクラスはfinalなのか?(Why String class is made final in Java)、もしくは単に、なぜStringはfinalなのか?(Why String is final)となります。これらの質問に答えるには、JavaプログラマはStringの動作に関して、このクラスの特別な機能と基本的な概念を、十分に理解している必要があります。StringはJavaでは神クラス(God class)で、いくつかの他クラスでは利用出来ない特別な機能を持ち、たとえばStringリテラルはプールされる、+演算子で文字列を連結できる、などがあります。Javaプログラミングにおける文字列の重要性を考慮し、JavaデザイナはStringクラスをfinalにし、これはjava.lang.Stringを拡張出来ないことを意味し、また、Stringオブジェクトをイミュータブルにもしています。最初の質問に戻ります。なぜJavaのStringはイミュータブルなのか?(Why String is Immutable in Java?) 当然ながら利点やメリットがあるからです。それでは、この決定に至った、利点や機能について考えていきましょう。この決定を解明可能なOracleやSunの公式ドキュメントがあるかどうか私は知りません。Javaの作者であるJames GoslingがStringクラスをfinalにする事について聞かれた際、セキュリティに関して答えていたことをどこかで読んだ記憶があります*2。クラスをfinalにすると拡張を強く制限することに対し反対意見があり、Jamesはその点についてコメントしており、Javaのセキュリティの保障のキーとなるクラスはfinalにすることで*3、振る舞いとJavaプラットフォームに対する不正な操作が変更不可能になります*4

5 Reasons of Why String is final or Immutable in Java

なぜStringクラスがfinalになったのかの本当の理由は、James Goslingが示唆するセキュリティ関連はともかくとして、Javaデザイナが一番良く知っています。そのため、以下に述べる理由は、私自身がなぜJavaのStringがfinalないしイミュータブルなのか、を考察したものになります。

1) String Pool

Javaデザイナはあらゆる種類のJavaアプリケーションで最も良く使われるデータ型はStringになるであろうと予測していたので、当初から最適化の要求がありました。最初のアイデアはStringプールにStringリテラルを格納することでした。オブジェクトを共有するこで、一時的なStringオブジェクトを削減することが目的で、そのためにはImmutable classである必要があります。互いに不可知な二つの領域にあるミュータブルなオブジェクトの共有は出来ません。例を考えてみます。二つの参照変数が同一のStringオブジェクトを指しているとします。

String s1 = "Java";
String s2 = "Java";

いま、s1を"Java"から"C++"オブジェクトに変更すると、参照変数はs2="C++"となります。Stringをイミュータブルにすることで、Stringリテラルの共有が可能となります。要するに、StringプールのアイデアはStringをfinalもしくはイミュータブルにしなければJavaでは実装出来ません。

2) Security

Javaはどのようなサービスレベルにおいてもセキュリティ環境を提供するという明確な目標があり、Stringはセキュリティ全体において重要です。Stringは多数のJavaクラスでパラメータとして広く使われており、たとえばネットワーク接続の開始のホストとポートや、ファイルとディレクトリのパスでJavaでファイルを読み込んだり、データベース接続に文字列としてURLを渡したり、を行います。もしStringがイミュータブルでなかったとしたら、ユーザはシステムの特定ファイルへのアクセス許可を得た後にPATHを変更可能で、これは重大なセキュリティ問題を引き起こします。同様に、ネットワーク上のマシンやデータベースへの接続中に、ミュータブルな文字列値はセキュリティ上の脅威になります。また、ミュータブルな文字列を引数に取るリフレクションでも同様にセキュリティ上の問題を引き起こします。

3) Use of String in Class Loading Mechanism

Stringをfinalないしイミュータブルにする他の理由としては、class loading mechanism.で頻繁に使われるから、という点もあります。Stringがイミュータブルでないとすると、攻撃者には利点を与えることになり、java.io.ReaderなどJavaの標準クラスのロードを悪意のあるcom.unknown.DataStolenReaderに変更が可能です。Stringをfinalかイミュータブルに保つことで、少なくともJVMは正しいクラスをロード可能です。

4) Multithreading Benefits

コンカレンシーとマルチスレッドはJavaの中核機能なので、Stringオブジェクトのスレッド安全性について考慮するのは当然のこととなりました。Stringは広く使われるようになることが予測されたので、イミュータブルにすることで外部からの同期化の必要を無くし、複数スレッド間でStringを共有するコードがクリーンになります。この機能により複雑でエラーになりやすいコンカレンシーなコードが簡易化されます。Stringはイミュータブルなのでスレッド間で共有可能で、結果として読みやすいコードになります。

5) Optimization and Performance

クラスをイミュータブルにする場合の利点は、クラスが一度生成されると変更が不可能になる点です。このことはキャッシュなどの多くのパフォーマンス最適化の道を開くことになります。String自身が変更されないことを知っているので、Stringはハッシュコードをキャッシュします。ハッシュコードの計算は遅延して行われ、一度作成されるとキャッシュされます。単純な場合では、StringオブジェクトのhashCode()メソッドの初回呼び出し時にハッシュコードが計算されて、それ以降のhashCodeは計算済みのキャッシュ値を返します。これにより、StringはHashtableとHashMapなどMapベースでハッシュが頻繁に使われる場合にはパフォーマンスが向上します。ハッシュコードのキャッシュは、String自身の内容に依存するので、イミュータブルおよびfinal無しには不可能です。

Pros and Cons of String being Immutable or Final in Java

上述の利点により、JavaでStringをfinalにすることによりもう一つの利点が生まれます。文字列はHashMapとHashtableなどハッシュベースのコレクションのキーとして最も使われます。イミュータブルはHashMapキーの必須要求ではありませんが、ミュータブルよりもイミュータブルオブジェクトを使う方がより安全で、その理由は、もしミュータブルオブジェクトの状態がHashMap内で変更される場合、equals()hashCode()メソッドは変更後の属性に基づくので、元に戻すのは困難です。クラスがイミュータブルな場合、ハッシュベースコレクション内に格納時に、状態変更のリスクはありません。別の重要な利点としては、既にスレッド安全性のところで触れました。Stringはイミュータブルなので、外部からの同期化を考慮することなくスレッド間で安全に共有可能です。これによりコンカレントなコードの可読性が高まりエラーの可能性を減らせます。

多くの利点がありますが、イミュータブルには欠点もあり、例えばコストがかかります。Stringはイミュータブルなので一時的に使用するオブジェクトを多数生成し、GCに影響を与えます。Javaデザイナもその点は認識しておりプールに格納するStringリテラルによりStringのゴミオブジェクトを削減できると考えていました。しかし、Stringプールからオブジェクトを取得しないnew String()コンストラクタを使わずにStringを生成するよう注意する必要があります。一般的なJavaアプリケーションは多数のオブジェクトを生成します。プールに格納するStringはGCに関連して隠れたリスクがあります。StringプールはJava Heapの、Java Heapに比べてかなり制限される、PermGen空間に配置されます。多数のStringリテラルを持つとすぐに空間を満たしてしまい、結果としてjava.lang.OutOfMemoryError: PermGen Spaceを引き起こします。ありがたいことに、Java言語のプログラマはこの問題を認識しておりJava 7以降ではStringプールは通常のヒープ空間に移動されました。こちらはPermGenに比べると非常に巨大です。Stringをfinalにする別の欠点としては、拡張性の制限です。滅多に必要にならないとはいえ、機能拡張のためにStringを拡張することは出来ず、java.lang.Stringクラスを拡張することを望む人々にとっては依然として制限事項の一つです。

上記5つの理由は間違いなくなぜJavaのStringクラスはfinalでイミュータブルなのか(Why String class has been made Final and Immutable in Java)に対するヒントになります。Of-course it's decision of Java designers but looks like above points contributes to take them this decision.*5同様な理由によりInteger, Long, Double, Floatなどのラッパークラスもイミュータブルでfinalです。