null値はどこに保存されますか、それともまったく保存されますか?


39

null値またはnull参照について学びたいです。

たとえば、Appleというクラスがあり、そのインスタンスを作成しました。

Apple myApple = new Apple("yummy"); // The data is stored in memory

それから私はそのリンゴを食べました、そして今、それはヌルである必要があるので、それをヌルとして設定します。

myApple = null;

この電話の後、私はそれを食べたことを忘れてしまったので、確認したいと思います。

bool isEaten = (myApple == null);

この呼び出しで、myAppleはどこを参照していますか?nullは特別なポインター値ですか?その場合、1000個のnullオブジェクトがある場合、ポインター型をintと見なすと、1000個のオブジェクトメモリスペースまたは1000個のintメモリスペースを占有しますか?

回答:


45

あなたの例でmyAppleは特別な値null(通常はすべてゼロビット)を持っているため、何も参照していません。元々参照していたオブジェクトは、ヒープ上で失われます。その場所を取得する方法はありません。これは、ガベージコレクションのないシステムでのメモリリークとして知られています。

最初に1000の参照をnullに設定した場合、通常は1000 * 4バイト(32ビットシステムでは64の2倍)の1000の参照だけのスペースがあります。これらの1000個の参照が元々実際のオブジェクトを指している場合、各オブジェクトのサイズの1000倍と、1000個の参照用のスペースを割り当てました。

一部の言語(CやC ++など)では、「初期化されていない」場合でも、ポインターは常に何かを指します。問題は、彼らが保持するアドレスがあなたのプログラムがアクセスするために合法であるかどうかです。特別なアドレスゼロ(別名null)は意図的にアドレス空間にマッピングされないため、メモリ管理ユニット(MMU)がアクセスされてプログラムがクラッシュすると、セグメンテーションフォールトが生成されます。ただし、アドレス0は意図的にマップされていないため、ポインターが何も指し示していないことを示すために使用する理想的な値になりますnull。ストーリーを完了するには、newまたはでメモリを割り当てますmalloc()、オペレーティングシステムはMMUを設定してRAMのページをアドレス空間にマップし、それらを使用可能にします。通常、マップされていないアドレス空間の範囲は依然として広範であるため、セグメンテーションエラーも発生します。


非常に良い説明。
-NoChance

6
「メモリリーク」の部分では少し間違っています。これは、自動メモリ管理のないシステムでのメモリリークです。ただし、GCが自動メモリ管理を実装する唯一の方法ではありません。C ++ std::shared_ptr<Apple>は、GCでもAppleゼロ化されたときにリークしない例です。
MSalters

1
@MSalters- shared_ptrガベージコレクションの基本的なフォームだけではありませんか?GCは、ガベージコレクションが発生することだけを別の「ガベージコレクター」が必要です。
モニカの復活

5
@Brendan:「ガベージコレクション」という用語は、通常のコードパスとは無関係に行われる非決定的コレクションを指すとほぼ一般的に理解されています。参照カウントに基づく決定論的な破壊は、まったく異なるものです。
メイソンウィーラー

1
良い説明。少し誤解を招く1つのポイントは、メモリ割り当てがRAMにマップされるという仮定です。RAMは短期的なメモリストレージの1つのメカニズムですが、実際のストレージメカニズムはOSによって抽象化されます。Windows(非リングゼロアプリの場合)では、メモリページは仮想化され、RAM、ディスクスワップファイル、または別のストレージデバイスにマップされる場合があります。
サイモンギルビー14

13

答えは、使用している言語によって異なります。

C / C ++

CおよびC ++では、キーワードはNULLで、実際にNULLが0でした。「0x0000」はオブジェクトへの有効なポインターにはならないため、それを示すために割り当てられる値が決定されました。有効なポインターではありません。しかし、それは完全に任意です。ポインタのようにアクセスしようとすると、メモリに存在しなくなったオブジェクトへのポインタとまったく同じように動作し、無効なポインタ例外がスローされます。ポインタ自体がメモリを占有しますが、整数オブジェクトが占有するだけです。したがって、1000個のNULLポインターがある場合、1000個の整数に相当します。これらのポインターの一部が有効なオブジェクトを指している場合、メモリの使用量は、1000の整数とそれらの有効なポインターに含まれるメモリに相当します。CまたはC ++では、メモリが解放されたことを意味するものではないため、dealloc(C)またはdelete(C ++)を使用してそのオブジェクトを明示的に削除する必要があります。

Java

CやC ++とは異なり、Javaではnullは単なるキーワードです。オブジェクトへのポインタのようにnullを管理するのではなく、内部的に管理され、リテラルのように扱われます。これにより、ポインターを整数型として結び付ける必要がなくなり、Javaでポインターを完全に抽象化できます。ただし、JavaがJavaをよりよく隠していても、ポインターであるため、1000個のNULLポインターは1000個の整数に相当する量を消費します。明らかに、オブジェクトがCやC ++のようにオブジェクトを指すと、それらのオブジェクトが参照するポインターがなくなるまでメモリが消費されますが、CやC ++とは異なり、ガベージコレクターは次のパスでそれを取得し、メモリを解放します。ほとんどの場合、解放されているオブジェクトと解放されていないオブジェクトを追跡する必要はありません(たとえば、オブジェクトを弱く参照する理由がない限り)。


9
あなたの区別は正しくありません。実際、CおよびC ++では、nullポインターはメモリアドレス0を指す必要はまったくありません(ただし、これはJavaおよびC#と同じ自然な実装です)。文字通りどこでも指すことができます。これは、リテラル-0が暗黙的にNULLポインターに変換される可能性があるという事実によって少し混乱しています。ただし、nullポインター用に格納されるビットパターンは、すべてゼロである必要はありません。
コンラッドルドルフ

1
いいえ、あなたは間違っています。セマンティクスは完全に透過的です…プログラムでは、nullポインターとマクロNULL(キーワードではなく)はゼロビットとして扱われます。しかし、それらをそのように実装する必要はなく、実際、いくつかのあいまいな実装ゼロ以外のヌルポインターを使用します。書くとif (myptr == 0)、nullポインターがで内部的に表されていても、コンパイラーは正しいことをし0xabcdefます。
コンラッドルドルフ

3
@Neil:NULLポインター定数(ゼロと評価される整数型の)は、NULLポインター値に変換可能です。(§4.10C ++ 11。)nullポインター値は、すべてのビットがゼロであるとは限りません。0はNULLポインター定数ですが、これはmyptr == 0すべてのビットmyptrがゼロかどうかをチェックすることを意味しません。
マット

5
@Neil:あなたが点検したいと思うかもしれないが、このエントリ Cよくある質問や、この SOの質問を
hugomg

1
@Neilだからこそ、NULLマクロをまったく言及せず、「nullポインター」について話し、「literal-0は暗黙的にnullポインターに変換できる」と明示的に言及することに苦労しました。
コンラッドルドルフ

5

ポインターは、ほとんどが整数型の単なる変数です。実際のオブジェクトが保存されるメモリアドレスを指定します。

ほとんどの言語では、このポインター変数を介してオブジェクトメンバーにアクセスできます。

int localInt = myApple.appleInt;

コンパイラは、のメンバーにアクセスする方法を知っていますApplemyAppleのアドレスへのポインタを「追跡」し、の値を取得しますappleInt

NULLポインターをポインター変数に割り当てた場合、ポインターはメモリーアドレスを指しません。(これにより、メンバーはアクセスできなくなります。)

ポインタごとに、メモリアドレス整数値を保持するメモリが必要です(32ビットシステムではほとんど4バイト、64ビットシステムでは8バイト)。これは、nullポインターにも当てはまります。


参照変数/オブジェクトは正確にはポインターではないと思います。それらを印刷する場合、ClassName @ Hashcodeが含まれます。JVMは内部的にHashtableを使用してHashcodeを実際のアドレスとともに保存し、必要に応じてHash Algorithmを使用して実際のアドレスを取得します。
-minusSeven

@minusSevenこれは、整数のようなリテラルオブジェクトに関係するものについて正しいです。それ以外の場合、ハッシュテーブルはAppleクラス自体に含まれる他のオブジェクトへのポインターを保持します。
ニール

@minusSeven:同意します。ポインター実装の詳細は、言語/ランタイムに大きく依存します。しかし、これらの詳細は特定の質問にそれほど関連していないと思います。
ステファン

4

簡単な例(可変名は保存されないことに注意してください):

void main()
{
  int X = 3;
  int *Y = X;
  int *Z = null;
} // void main(...)


...........................
....+-----+--------+.......
....|     |   X    |.......
....+-----+--------+.......
....| 100 |   3    |<---+..
....+-----+--------+....|..
........................|..
....+-----+--------+....|..
....|     |   Y    |....|..
....+-----+--------+....|..
....| 102 |  100   +----+..
....+-----+--------+.......
...........................
....+-----+--------+.......
....|     |   z    |.......
....+-----+--------+.......
....| 104 |   0    |.......
....+-----+--------+.......
...........................

乾杯。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.