はじめに
プログラミング学習を始めて1年経過しました。しかし、今までオブジェクト指向の概念から目を背けてきた結果、現在エンジニアとして苦労しているので、書籍「オブジェクト指向でなぜつくるのか」にて学習中です。
その中で、以下のような章がありました。
「メモリの仕組みの理解はプログラマのたしなみ」
これを見て、自分は今までそんなこと意識していなかったのと強く感じたので、今回はこの書籍で書かれていた、一般的なプログラムの動作環境としての最低限の知識を整理しました。
プログラムが動く仕組みを理解する上で重要な概念
コンパイラ方式とインタプリタ方式
プログラムの基本的な実行方式は大きく分けて2つあります。
コンパイラ方式
- コンパイラって何?
- コンパイラは、プログラム全体を読み込んで、それをコンピュータが理解できる形(機械語)に一度に変換するプログラムです。
- どういう時に使うの?
- コンパイラは、プログラムを書き終えた後に使います。あなたが書いたプログラムをコンパイラが読んで、コンピュータが実行できる形にします。
- メリット
- コンパイルされたプログラムは通常、速く動きます。なぜなら、コンピュータが理解できる言語にすでに翻訳されているからです。
- デメリット
- エラーを見つけたり、プログラムに変更を加えたりするたびに、プログラムを再コンパイル(翻訳)する必要があります。
インタプリタ方式
- インタプリタって何?
- インタプリタは、プログラムを一行ずつ読み、その都度、コンピュータが理解できる形に変換して実行します。
- どういう時に使うの?
- インタプリタは、プログラムを書いている最中に使います。一行ずつ実行してみることで、その場で結果を確認できます。
- メリット
- エラーを見つけやすく、修正が迅速に行えます。また、異なる種類のコンピュータやOSでも同じコードが動くことが多いです。
- デメリット
- コンパイラ方式に比べて実行速度が遅い場合があります。なぜなら、プログラムの各行を実行するたびに翻訳する必要があるからです。
まとめ
- コンパイラ方式: あらかじめ全てを翻訳して、速く実行する方法。
- インタプリタ方式: 逐次的に翻訳しながら実行する方法で、エラーの修正が容易。
以下の記事は図解もあってイメージしやすかったです。
仮想マシン
先ほど、実行方式には大きく分けてコンパイラ方式とインタプリタ方式の2つがあると記載しましたが、例えばJavaなどはこの2つのどちらでもない「中間コード方式」と呼ぶ方式を採用しているらしいです。
- 中間コードって何?
- Javaのような言語では、プログラムはまず「中間コード」と呼ばれる形式に変換されます。この中間コードは、通常のコンピュータが直接理解できる機械語ではなく、特定の仮想マシン(例えばJavaの場合はJava Virtual Machine、略してJVM)が理解できる形式です。
- プロセス
- Javaプログラムは最初にコンパイラによってこの中間コード(Javaでは「バイトコード」と呼ばれます)に変換されます。その後、実行時にJVMがこのバイトコードを読み込み、それを実際の機械語に変換して実行します。
- プラットフォーム独立性
- 中間コード方式の最大の利点は、一度書かれたプログラムが様々な種類のコンピュータやオペレーティングシステムで実行できることです。Javaのプログラムは、どんなコンピュータ上でも、JVMがあれば実行可能です。
- メリット
- 書いたコードが様々なシステムで動く「書き一度、どこでも実行」が可能。また、実行時のセキュリティや効率性が向上します。
- デメリット
- コンパイラ方式や純粋なインタプリタ方式に比べると、実行速度が多少遅くなることがあります。これは、実行時に中間コードを機械語に変換する工程が追加されるためです。
中間コード方式の説明の中でも出てきましたが、JVMのような仮想マシンは、特定のプログラムや言語を実行するための環境を提供します。JVMはJavaプログラムのための仮想環境であり、そのおかげでJavaプログラムはどんなコンピュータでも同じように動作することができます。これにより、開発者は異なるプラットフォームでの互換性を気にせずに済みます。
スレッド
まず最初に、プロセスとスレッドについて整理します。
プロセスとは
- プロセスの定義
- プロセスは、実行中のプログラムです。コンピュータ上でプログラムを実行すると、そのプログラムは「プロセス」として実行されます。
- プロセスの特徴
- 各プロセスは独自のメモリ空間を持ち、プログラムのコードや実行中のデータを含みます。プロセス間ではメモリが分離されているため、一つのプロセスが他のプロセスに影響を与えることは通常ありません。
スレッドとは
- スレッドの定義
- スレッドは、プロセス内で実行される「軽量の実行単位」です。一つのプロセスは一つ以上のスレッドを持ち、これらのスレッドはプロセスのリソース(メモリなど)を共有しながら動作します。
- スレッドの特徴
- スレッドはプロセスの中で実際の作業を行います。例えば、一つのプログラム内で同時に複数のタスクを行いたい場合、それぞれのタスクを異なるスレッドに割り当てることができます。
以下の記事では図解もありわかりやすいです。
マルチスレッド環境
スレッドは、一つのプロセス内に複数存在でき、同時に並行して動くことが可能です。しかし厳密にいうと、CPUが一時点で実行できる処理は一つだけなので、複数のスレッドの処理を少しづつ順繰りに実行しているということになります。
マルチスレッド環境とは、コンピュータが同時に複数のスレッド(つまり、プログラムの異なる部分)を処理できる状態のことです。ここでいう「同時」は、非常に高速にスレッド間を切り替えているため同時のように見える場合のことになります。
これにより、コンピュータのリソースをより効率的に活用し、アプリケーションのパフォーマンスを向上させることができるみたいです。
以下の記事では図解もありわかりやすいです。
メモリの使い方の説明
一般的なメモリの使い方
コンピュータのメモリは、プログラムが動作するために必要なデータを保持する場所です。このメモリ領域は大きく分けて「静的領域」、「ヒープ領域」、「スタック領域」の三つに分類されます。
静的領域
- 定義:
- 静的領域は、プログラムの実行中ずっと存在するデータを保持する領域です。
- 使用例:
- グローバル変数やプログラムを実行可能な形式に変換したコード情報がこの領域に格納されます。
ヒープ領域
- 定義:
- プログラム実行時に動的に確保するためのメモリ領域を指します。
- 使用例:
- アプリケーションから要求されたら、必要なサイズのヒープ領域を割り当て、不要になれば解放します。複数のスレッドから同時に割り当てを要求されても整合性を保てるように、OSや仮想マシンが管理機能を提供しています。
スタック領域
- 定義:
- 関数の呼び出しとローカル変数(関数内で宣言された変数)を管理するためのメモリ領域です。
- 使用例:
- 呼び出したサブルーチン(関数)の処理が終わる前に、サブルーチン(関数)が次のサブルーチン(関数)を呼び出す入れ子構造になるので、 LIFO方式(後入れ先出し方式)によりメモリを効率的に使用できるようになっている。
このスタック領域はスレッドに1つずつ用意される。
- 呼び出したサブルーチン(関数)の処理が終わる前に、サブルーチン(関数)が次のサブルーチン(関数)を呼び出す入れ子構造になるので、 LIFO方式(後入れ先出し方式)によりメモリを効率的に使用できるようになっている。
下記記事を参照
終わりに
ここまで、一般的なプログラムの動作環境として、コンパイラとインタプリタ、仮想マシン、スレッド、メモリ管理の説明を整理しましたが、これらの内容はプログラマにとって「たしなみ」以前の「常識」らしいです…何も理解していませんでした😢笑
余談ですが、オブジェクト指向で書いたプログラムについては、基本的な仕組みは上記で整理した内容と同じらしいですが、メモリの使い方がだいぶ変わってくるとのことです。それは引き続き学習したいと思います。