オブジェクトの概念
Assume は、オブジェクトのメモリ アドレスを取得するメソッドgetObjectAddress(Object)
です。
最初の質問:
次のコードを検討してください。
public static void main(String[] args) {
Object o = new Object();
long address1 = getObjectAddress(o);
// .......
long address2 = getObjectAddress(o);
}
main
このメソッドでは、Object オブジェクトが作成され、オブジェクトのアドレスgetObjectAddress
を取得するために。2 回取得したオブジェクト アドレスが異なる可能性はありますか? つまり、オブジェクトのアドレスが変更される可能性はありますか?
答え: 可能です。JVM には GC または「ガベージ コレクション」メカニズムがあり、使用されなくなったオブジェクトをリサイクルしてメモリ領域を解放します。GC はオブジェクトを移動することがあります。
2 番目の質問:
次のコードを検討してください。
private static long allocate() {
Object o = new Object();
return getObjectAddress(o);
}
public static void main(String[] args) {
long address1 = allocate();
// ......
long address2 = allocate();
}
allocate()
Object オブジェクトが作成され、そのオブジェクト アドレスが取得されます。main
method allocate()
、2つのオブジェクトのメモリアドレスが同じである可能性はありますか?
答え: 可能です。allocate()
メソッドで作成されたオブジェクトは、メソッドが戻った後にすべての参照を失い、「不要になったオブジェクト」になります. 2 つのメソッド呼び出しの間に、最初のメソッド呼び出しで生成された一時オブジェクトが上記の GC メカニズムによってクリアされる場合、リサイクル、対応するオブジェクトメモリ空間が「空き」になり、他のオブジェクトが占有できるようになります。
3 番目の質問:
あ、同じオブジェクトのメモリアドレスが違う可能性があると言われているので、2つの異なるオブジェクトが同じメモリアドレスを持っている可能性があり、Javaではオブジェクトのメモリアドレスを判断する==
ことです
Object o = new Object();
if (o != o)
そのほか
Object o1 = new Object();
Object o2 = new Object();
if (o1 == o2)
ここで両方可能if
ですか?
答え: 不可能です。==
演算子がオブジェクト アドレスを比較するのは事実ですが、実際には次の 2 つの条件が暗示されています。
- この演算子は、「その瞬間」の2 つのオブジェクトのアドレスを比較します。
- 比較される両方のオブジェクトは同じプロセスにあります。
上記の 2 つの場合はどちらも「同時に」という条件を満たさないため、2 つの if が真になることはありません。
クラスとメソッド
4 番目の質問:
Framework が Android Framework のクラスで、App が Android アプリのクラスであるとします。
public class Framework {
public static int api() {
return 0;
}
}
public class App {
public static void main(String[] args) {
Framework.api();
}
}
アプリをコンパイルしFramework
、api
内部メソッドの戻り値の型を int から long に変更し、フレームワークをコンパイルするがアプリを再コンパイルしない場合、アプリはフレームワークの API メソッドを正常に呼び出すことができますか?
答え: いいえ。Javaクラスに格納されている呼び出されたメソッドの情報には、戻り値の型が含まれており、戻り値の型が間違っていると、実行時に対応するメソッドを見つけることができません。メソッドをメンバー変数に変更してから、その変数の型を変更する場合も同様です。
5 番目の質問:
次のコードを検討してください。
class Parent {
public void call() {
privateMethod();
}
private void privateMethod() {
System.out.println("Parent method called");
}
}
class Child extends Parent {
private void privateMethod() {
System.out.println("Child method called");
}
}
new Child().call();
子の子は親の子をオーバーライドしますprivateMethod
か? call
親の呼び出しは、親または子で呼び出されますprivateMethod()
か?
回答: メソッドの書き換えを構成するものではなく、 Parent で引き続き呼び出されますprivateMethod
。プライベート メソッドはダイレクト メソッドであり、ダイレクト メソッドはオーバーライドできません。
オペレーティング システムの基本
マルチプロセッシングと仮想メモリ
工程Aと工程Bがあるとします。
6番目の質問:
プロセスAのオブジェクトaとプロセスBのオブジェクトbが同じメモリアドレスを持っていますが、同じオブジェクトですか?
回答:もちろんありません.「オブジェクトの平等」の概念は同じプロセスでのみ意味があると上で述べました.クラスを注意深く聞いて考えないと、スパンキングされます〜
7番目の質問:
プロセス A にオブジェクト a があり、このオブジェクトのメモリ アドレスが B に渡されます。B はこのオブジェクトに直接アクセス (読み取り、書き込みなど) できますか?
回答: いいえ、セグメンテーション違反が発生する可能性は高く、メモリ空間内の間違ったオブジェクトのデータが変更される可能性はわずかですが、プロセス A には影響しません。ユーザー空間で動作するプロセスとして、プロセスが取得するいわゆるメモリアドレスはすべて仮想アドレスであり、プロセスがこれらのアドレスにアクセスするとき、プロセスはまず変換プロセスを経て物理アドレスに変換してから動作します。変換がうまくいかない場合 (指定したアドレスがまったくわからない場合、または対応するメモリのアクセス許可によって対応する操作が実行できない場合)、セグメント フォールトがトリガーされます。
8 番目の質問:
これはまだ素敵なプロセス A と B ですが、今回は B が A の子プロセスです。つまり、A は fork を呼び出して新しいプロセス B を生成します。
void a() {
int* p = malloc(sizeof(int));
*p = 1;
if (fork() > 0) {
// 进程 A 也即父进程
// 巴拉巴拉巴拉一堆操作
} else {
// 进程 B 也即子进程
*p = 2;
}
}
(fork は Posix でプロセスを作成するための API です。呼び出しが完了した後、まだ親プロセスにある場合、子プロセスの pid は常に 0 より大きくなり、子プロセスでは 0 を返します)
(それでもわからない場合は、A を Zygote プロセス、B を任意の App プロセスと想像してください)
このコードは、メモリの一部を割り当て、fork を呼び出して子プロセスを生成し、事前に割り当てられたメモリの値を子プロセスで 2 に変更します。質問: プロセス B によって行われた変更は、プロセス A に表示されますか?
回答: 表示されません。プロセス A によって表示されるメモリのセクションの値は、まだ 1 です。Linux カーネルには「コピー オン ライト」という技術があります.プロセス B がこのメモリ セクションに書き込もうとすると、密かに実メモリをコピーし、最後に書き込まれるのはこのコピー内のコピーです.値ですが、プロセス Aまだ元の値が表示されます。
クロスプロセスのビッグデータ転送
既知のプロセス A とプロセス B、プロセス A は AIDL インターフェイスを公開し、プロセス B は A から 10M データを取得しようとしており (バインダーのデータ サイズ制限をはるかに超えています)、ファイル パスを渡すことは禁止されており、この AIDL の呼び出しのみが許可されています。インターフェースは一度、それを達成する方法は?
回答: ファイル記述子 (ファイル記述子) を渡すことができます。これがファイルだけを表すとは思わないでください! たとえば、アプリケーション層の開発者は、共有メモリ メソッドを使用して AIDL 実装クラスを記述し、データを渡すことができます。
@Override public SharedMemory getData() throws RemoteException {
int size = 10 * 1024 * 1024;
try {
SharedMemory sharedMemory = SharedMemory.create("shared memory", size);
ByteBuffer buffer = sharedMemory.mapReadWrite();
for (int i = 0;i < 10;i++) {
// 模拟产生一堆数据
buffer.put(i * 1024 * 1024, (byte) 114);
buffer.put(i * 1024 * 1024 + 1, (byte) 51);
buffer.put(i * 1024 * 1024 + 2, (byte) 4);
buffer.put(i * 1024 * 1024 + 3, (byte) 191);
buffer.put(i * 1024 * 1024 + 4, (byte) 98);
buffer.put(i * 1024 * 1024 + 5, (byte) 108);
buffer.put(i * 1024 * 1024 + 6, (byte) 93);
}
SharedMemory.unmap(buffer);
sharedMemory.setProtect(OsConstants.PROT_READ);
return sharedMemory;
} catch (ErrnoException e) {
throw new RemoteException("remote create shared memory failed: " + e.getMessage());
}
}
次に、プロセス B で次のように取得します。
IRemoteService service = IRemoteService.Stub.asInterface(binder);
try {
SharedMemory sharedMemory = service.getData();
ByteBuffer buffer = sharedMemory.mapReadOnly();
// 模拟处理数据
int[] temp = new int[10];
for (int i = 0;i < 10;i++) {
for (int j = 0;j < 10;j++) {
temp[j] = buffer.get(i * 1024 * 1024 + j);
}
Log.e(TAG, "Large buffer[" + i + "]=" + Arrays.toString(temp));
}
SharedMemory.unmap(buffer);
sharedMemory.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
ここで使用する SharedMemory は Android 8.1 から利用可能で、8.1 以前のシステムでも利用可能な MemoryFile という API があります。SharedMemory のソース コードを開くと、実際に ashmem (匿名共有メモリ) が作成され、対応するファイル記述子がバインダーに渡されることがわかります。カーネルは、利用可能なファイル記述子をターゲット プロセスに渡します。プロセス間で渡すことができるファイル ストリームとして理解できます (パーミッション チェックを通過できる限り)。このガジェットを合理的に使用すると、奇跡的な効果が得られます :)