インスタンスを作るって言うけど、パソコンのどこにできるの?
はじめに
こんにちは。株式会社divxのエンジニア高橋です。
突然ですが、「インスタンスはパソコンのどこにできるの?」と聞かれたら皆さんはなんと答えるでしょうか?
この質問に対してパッと答えられない方も中にはいると思います。私自身そうでした…
今の時代、プログラミングをRubyなどから学習した方も多く、Rubyの様な高級言語ではプログラムが裏側でどの様に動くかを意識しなくても開発できてしまいます。
しかし裏側の知識がない状態で開発をしてしまうと、低レイヤーの障害が起きた時にわけが分からず、解決にたどり着けない場合があります。
また裏側の仕組みを知ることで、自分が作っているプログラムを新しい角度から見ることができ、もっとプログラミングが楽しくなるはずです。
本記事では、裏側の仕組みの中で「インスタンスが作られる場所」についてフォーカスを当てて解説していきます。
目次[非表示]
インスタンスが作られる場所とは?
それでは、結論から行きましょう。
スバリ!インスタンスが作られる場所は「ヒープ領域」という場所です。
いきなり聞き慣れない言葉が出てきましたね。ヒープ領域という言葉、ここで初めて聞いたという方もいるのでは無いでしょうか?
まずは「ヒープ領域とは何か?」を説明していきたいと思います。ただ、ヒープ領域を理解するためには、メモリも一緒に知る必要がありますので、メモリと一緒に解説していきます。
メモリとは?
メモリとは主記憶装置と呼ばれ、CPUから直接読み書きができる記憶装置です。プログラムを動かすときは、メモリの領域を使いながらプログラムを実行していきます。
メモリの内部を見ていくと、メモリには大きく「スタック領域」「静的領域」「ヒープ領域」の3つの領域に分かれます。
簡単に、各領域が何を担当しているのかを解説していきます。
スタック領域
スタック領域とは、プログラムの実行時に動的に確保されるメモリ領域で、一時的な記憶領域として使われます。スレッドに対して、スレッド終了までメモリ領域を割り当て、スレッド1つ1つに領域が確保されます。
スタック領域には「スレッド上で実行されるメソッド」や「メソッドが使用するローカル変数」の一時的なデータが置かれます。
静的領域
静的領域とは、プログラム開始時に確保され、プログラムが終了するまで配置が固定されます。
静的領域には「定数」や「グローバル変数」が置かれます。
ヒープ領域
ヒープ領域とは、プログラムの実行時に動的に確保されるメモリ領域で、プログラムの実行中にアプリケーションから必要なサイズを要求することで割当を行います。
ヒープ領域には「インスタンス」が置かれます。
インスタンスがヒープ領域に作られる理由ですが、インスタンスを作るということは、new演算子で「アプリケーションから必要なサイズを要求する」という事になります。そのため、インスタンスはヒープ領域に作られていきます。
では、実際にコードを使ってインスタンスが作られる具体的な流れを見て行きましょう。
ここからはJavaのコード使って解説をしていきますが、コードの説明を入れていますので、Javaを知らない人でも安心してお読みください。
インスタンスが作られる具体的な流れ
インスタンスが作られる具体的な流れを下記のコードで見ていきます。
◎Javaコードの説明
こちらのコードではnew Stringという記述でStringクラスから「これはサンプルです」という文字列のインスタンスを生成しています。
生成したStringクラスのインスタンスをString型のsampleというローカル変数に格納しています。
では、裏側ではどのような流れになっているのかを解説していきます。
まず、new演算子でヒープ領域にインスタンスを確保する為の領域が確保されます。コードを見ると、new Stringとなっているため、Stirngクラスのインスタンスが作られる場所をヒープ領域に確保しています。
次に、String sample = new String(”これはサンプルです”) とローカル変数のsampleに格納しています。
ローカル変数はスタック領域に置かれるので、 sampleにはヒープ領域にあるインスタンスのポインタが格納されます。
まとめると、スタック領域にはsampleというローカル変数があり、中には「これはサンプルです」のStringクラスのインスタンスを示すポインタが入っています。
ヒープ領域には「これはサンプルです」のStringクラスのインスタンスが生成されています。
インスタンスが作られる具体的な流れを説明してきましたが、言葉で言われても「本当にヒープ領域が使われてるの?」と疑問に思う方もいるかと思います。
なので、実際にメモリの中を覗いて「本当にヒープ領域が使われているのか?」を確かめて行きましょう。
本当にヒープ領域使われてる?JConsoleを使ってヒープ領域を覗いてみる
ヒープ領域にインスタンスが作られるという事は、インスタンスを生成すればヒープ領域の使用率は上がっていくはずです。ヒープ領域の使用率はJConsoleを使うと見ることができるので、JConsoleを使って見ていきたいと思います。
JConsoleとは
JConsoleは、Java Management Extensions (JMX) 仕様に準拠した監視ツールです。
Java プラットフォームで実行されるアプリケーションのパフォーマンスとリソース消費に関する情報を提供してくれます。
インスタンスを無限に生成するJavaプログラムを実行する
インスタンスを無限に生成する下記のJavaプログラムを実行させ、JConsoleからヒープ領域の使用率を見ていきます。
◎Javaコードの説明
while(true)で無限ループを作り、new Factory()でFactoryクラスからインスタンスを生成しています。
Thread.sleepという記述で、0.5秒処理がストップするため、0.5秒に1回Factoryクラスからインスタンスが無限に生成されるプログラムになります。
JConsoleを使ってヒープ領域の使用率を見る
まず、無限にインスタンスを生成するJavaプログラムを実行させます。
次に、ターミナルにJConsoleと入力しJConsoleを起動していきます。
JConsoleが起動したら、無限にインスタンスを生成するJavaプログラムに接続し、ヒープ領域の使用率を確認します。
画像を見てみると、時間が経てば経つほどヒープ領域の使用率が上がっているのが分かります。
この事から、インスタンス生成でヒープ領域が使われているのが分かって頂けたかと思います。
ヒープ領域の使用率が増えているのは分かったが、「ヒープ領域にインスタンスが作られているのか、これだと分からない」
そんな声が聞こえて来そうなので、次は「本当にヒープ領域にインスタンスが作られているのか?」を確認していきましょう。
jcmdユーティリティの「jcmd GC.class_histogram [-all]」を使って実際にヒープ領域にインスタンスが作られているのか?を確認してみます。
本当にヒープ領域にインスタンス作られてる?jcmdユーティリティを使ってヒープ領域を覗いてみる
ヒープ領域にインスタンスが作られているのか?は「jcmdユーティリティ」を使う事で確認することができます。
jcmdユーティリティとは?
JDKの中には、Javaアプリケーションに関する問題を診断するため、jcmdユーティリティが導入されており、jcmdユーティリティを使用する事で、指定されたプロセスやコアファイルのヒープ領域の詳細を表示ことができます。ヒープ領域の詳細を表示するには、jcmdユーティリティの「GC.class_histogramコマンド」を使って表示します。
Jcmdユーティリティを使ってヒープ領域の中を覗く
再度、無限にインスタンスを作るJavaプログラムを実行します。
「jcmd <pid> GC.class_histogram -all」こちらのコマンドのpidの部分に無限にインスタンスを生成するJavaプログラムのプロセスIDを入力し、ターミナルで実行します。
すると画像のような情報が表示されます。
instancesと書かれている数字は「インスタンスの個数」を表しており、class nameは「クラス名」を表しています。
インスタンスを無限に作るJavaプログラムを少しの間動かし続け、jcmd <pid> GC.class_histogram -allコマンドを実行しヒープ領域の中を確認してみます。
すると、Factoryクラスのインスタンス数が「103個」と表示されているのが分かります。
また少し時間を置いて、再度jcmd <pid> GC.class_histogram -allコマンドを実行します。
すると、Factoryクラスのインスタンス数が「332個」と増えている事がわかります。
この事から、インスタンスはヒープ領域に作られている事を確認できます。
インスタンスが作られる場所は「ヒープ領域」という場所です。
この言葉が分かって頂けたかと思います。
終わりに
本記事では、インスタンスが作られる場所の「ヒープ領域」についてご紹介してきました。
実際にヒープ領域の中身を見ることで、プログラムの裏側が身近に感じられたのではないでしょうか?
今までは、何気なく作っていたインスタンスも、これからは「newメソッドでヒープ領域を確保して…」と新たな視点でコードを見ることができると思います。
新たな視点でコードを見ることができると、もっとプログラミングが楽しくなりますね。
この記事をきっかけに、裏側の仕組みに興味を持ってくれると嬉しいです。
divxでは一緒に働ける仲間を募集しています。
興味があるかたはぜひ採用ページを御覧ください。
以上、高橋がお送りしました!
読んで頂きありがとうございました。
実行環境
MacBookAir(M1,2020)
macOS Big Surバージョン11.5.2
Darwin XA-030.local 20.6.0 Darwin Kernel Version 20.6.0: Wed Jun 23 00:26:27 PDT 2021; root:xnu-7195.141.2~5/RELEASE_ARM64_T8101 x86_64
JDK(Java Development Kit)
AdoptOpenJDK version11
java -version
openjdk version "11.0.14" 2022-01-18
OpenJDK Runtime Environment Temurin-11.0.14+9 (build 11.0.14+9)
OpenJDK 64-Bit Server VM Temurin-11.0.14+9 (build 11.0.14+9, mixed mode)
参考
[メモリ]東京工業大学 C言語C++言語 メモリの動的確保
[スタック領域]8.1.5 スタック
[ヒープ領域]SerialGC使用時のJavaVMで使用するメモリ空間の構成とJavaVMオプション
[JConsole]JConsoleの使用 - Java SEモニタリングおよび管理ガイド
[Jcmdユーティリティ]診断ツール
オブジェクト指向でなぜ作るのか(書籍)
基礎からわかるTCP/IPネットワークコンピューティング入門(書籍)