インタビューでは、人々はしばしばその中にどんな方法があるのかを尋ねます。 このクラスをより詳しく見る時間です。 私が最初に疑問に思ったのは、Javaソースにjava.lang.Objectクラスがあるかどうかでした。 結局のところ、それは珍しいことです。最上位のもののように、実装にしっかりと縫い付けることができます。
ただし、そのようなクラスがあり、ここではソースコードjava / lang / Object.javaを提供し、javadocを省略して、jvm実装に関連するいくつかのポイントを明らかにします。
package java.lang; public class Object { private static native void registerNatives(); static { registerNatives(); } public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } protected native Object clone() throws CloneNotSupportedException; public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); } protected void finalize() throws Throwable { } }
このコードで何を指摘したいですか?
合計で、Objectには11個のパブリックメソッドがあり、5個は通常メソッド、6個はネイティブ実装です。
それらのコードはすでに利用可能であるため、通常の方法を検討してください。
デフォルトでは、すべてのオブジェクトのリンクの等価性が比較されます。 ところで、C ++プログラマを混乱させるために、Javaのポインタはリンクと呼ばれるというジョークが好きでした。
public boolean equals(Object obj) { return (this == obj); }
toStringには、hashCode()が16進文字列に変換されることを除いて、異常なものも含まれていません。 そして、もしapanginがhashCodeを計算することが不可能だと書いていなかったら、初期のJavaプログラマーはhashCodeでオブジェクトを見つけることができると思っていたでしょう。 彼は参照に過ぎませんでした。 多くの場合、これらの32ビット時間は過ぎました。今では、toString()でhashCodeを出力するのが理にかなっているかどうかさえわかりません。
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
待機がマルチスレッドを提供するプリミティブに属しているという事実に加えて、nanosパラメーターの無用性に注意したいと思います。
場合によっては、1ミリ秒を追加するだけです。 興味深いことに、これは将来のブックマークであるか、待機(長いタイムアウト、int nanos)の実装が異なるシステムが既に存在します。
public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); }
java.lang.Objectの通常のメソッドのパレードを完了します。
protected void finalize() throws Throwable { }
このメソッドは何も実行せず、 finalizeおよびFinalizerを使用することを避けるべき多くのマテリアルがあります。
では、java / lang / Object.classを見てみましょう、たとえば、スーパークラスとはどういうものなのでしょうか。 インストール済みのjreまたはjdk rt.jarを見つけて解凍します。
jar -xf rt.jar
そして、00 00が彼のスーパークラスで記述されていることがわかります。スーパークラスなしで手でクラスファイルを作成するとどうなりますか。
以前の投稿からHello.classを取りました。
それをvimで開き 、バッファーの内容をvim.wikia.com/wiki/Hex_dumpの 16進ダンプに置き換えました :
:%!xxd
私はvimエディターの力に驚きました。 super_classのバイトをすばやく見つけました。 constant_poolの終了後4バイトの仕様に従っていることを思い出させてください。 constant_poolの末尾は、文字列00 01のタグとゼロが始まる一連の非ゼロバイトによって検索されます。ゼロが始まると、constant_poolの他のセクションに進みます。 他のクラスファイルの場合、これは当てはまらないかもしれませんが、私の場合はうまくいきました。
バイナリ形式に戻ります。
:%!xxd -r
変更を保存します。 固定アプリケーションを起動します。
java -cp classes/ hello.App Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.ClassFormatError: Invalid superclass index 0 in class file hello/App at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
エラーでさえ、一部ではありませんが、クラスのロード中にネイティブメソッドからスローされます。 対処しましょう。1つは、このようなエラーをスローする方法を理解できるためです。
jdkソースが必要です。 私は研究のためにOpenJDKを選びました。 ここからそれらをスイングします。
hg.openjdk.java.net/jdk8/jdk8
そしてMercuryに保存します:
hg clone hg.openjdk.java.net/jdk8/jdk8
しかし、それだけではありません。
以下も実行する必要があります。
./get_source.sh
待って。 ソースがダウンロードされたので、エラーを検索できます。 私はこのgrepをしています:
grep -nr 'Invalid superclass index' * hotspot/src/share/vm/classfile/classFileParser.cpp:3095: "Invalid superclass index %u in class file %s", hotspot/src/share/vm/classfile/classFileParser.cpp:3100: "Invalid superclass index %u in class file %s",
classFileParser.cppを開き、行3095にあります。
instanceKlassHandle ClassFileParser::parse_super_class(int super_class_index, TRAPS) { instanceKlassHandle super_klass; if (super_class_index == 0) { check_property(_class_name == vmSymbols::java_lang_Object(), "Invalid superclass index %u in class file %s", super_class_index, CHECK_NULL); } else { check_property(valid_klass_reference_at(super_class_index), "Invalid superclass index %u in class file %s", super_class_index, CHECK_NULL); // The class name should be legal because it is checked when parsing constant pool. // However, make sure it is not an array type. bool is_array = false; if (_cp->tag_at(super_class_index).is_klass()) { super_klass = instanceKlassHandle(THREAD, _cp->resolved_klass_at(super_class_index)); if (_need_verify) is_array = super_klass->oop_is_array(); } else if (_need_verify) { is_array = (_cp->unresolved_klass_at(super_class_index)->byte_at(0) == JVM_SIGNATURE_ARRAY); } if (_need_verify) { guarantee_property(!is_array, "Bad superclass name in class file %s", CHECK_NULL); } } return super_klass; }
この部分に興味があります:
if (super_class_index == 0) { check_property(_class_name == vmSymbols::java_lang_Object(), "Invalid superclass index %u in class file %s", super_class_index, CHECK_NULL); }
check_propertyはヘッダーファイルclassFileParser.hppにあり、次のようになります。
inline void check_property(bool property, const char* msg, int index, TRAPS) { if (_need_verify) { guarantee_property(property, msg, index, CHECK); } else { assert_property(property, msg, index, CHECK); } }
_need_verifyが設定されている場所とその原因を探し始めました。 classFileParser.cppには次の行があることがわかりました。
_need_verify = Verifier::should_verify_for(class_loader(), verify);
呼び出されたときに検証が渡されます:
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, KlassHandle host_klass, GrowableArray<Handle>* cp_patches, TempNewSymbol& parsed_name, bool verify, TRAPS)
このメソッドは多くの場所で呼び出されますが、ホットスポット/ src / share / vm / classfile / classLoader.cppに興味があります。
instanceKlassHandle result = parser.parseClassFile(h_name, loader_data, protection_domain, parsed_name, false, CHECK_(h));
hotspot / src / share / vm / classfile / verifier.cppでのshould_verify_forの動作:
bool Verifier::should_verify_for(oop class_loader, bool should_verify_class) { return (class_loader == NULL || !should_verify_class) ? BytecodeVerificationLocal : BytecodeVerificationRemote; }
should_verify_classにfalseを渡すため、hotspot / src / share / vm / runtime / arguments.cppでBytecodeVerificationLocalを確認します。
// -Xverify } else if (match_option(option, "-Xverify", &tail)) { if (strcmp(tail, ":all") == 0 || strcmp(tail, "") == 0) { FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, true); FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, true); } else if (strcmp(tail, ":remote") == 0) { FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, false); FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, true); } else if (strcmp(tail, ":none") == 0) { FLAG_SET_CMDLINE(bool, BytecodeVerificationLocal, false); FLAG_SET_CMDLINE(bool, BytecodeVerificationRemote, false); } else if (is_bad_option(option, args->ignoreUnrecognized, "verification")) { return JNI_EINVAL; } // -Xdebug }
さらに掘り下げると、ホットスポット/ src / share / vm / runtime / globals_extension.hppのマクロで黒魔術を見つけることができます:
#define FLAG_SET_CMDLINE(type, name, value) (CommandLineFlagsEx::type##AtPut(FLAG_MEMBER_WITH_TYPE(name,type), (type)(value), Flag::COMMAND_LINE)) class CommandLineFlagsEx : CommandLineFlags { public: static void boolAtPut(CommandLineFlagWithType flag, bool value, Flag::Flags origin); static void intxAtPut(CommandLineFlagWithType flag, intx value, Flag::Flags origin); static void uintxAtPut(CommandLineFlagWithType flag, uintx value, Flag::Flags origin); static void uint64_tAtPut(CommandLineFlagWithType flag, uint64_t value, Flag::Flags origin); static void doubleAtPut(CommandLineFlagWithType flag, double value, Flag::Flags origin); static void ccstrAtPut(CommandLineFlagWithType flag, ccstr value, Flag::Flags origin); static bool is_default(CommandLineFlag flag); static bool is_ergo(CommandLineFlag flag); static bool is_cmdline(CommandLineFlag flag); };
しかし、これにはまだ興味がありません。 jvmが-Xverifyパラメーターなしで起動する場合、BytecodeVerificationLocalの値を見つける必要があります。 これはコード内で見つけることができますが、ダービーをさらに登るのは今では適切ではないようで、脱出する時が来ました。 役立つドキュメント。 デフォルトでは、jvmは-Xverify:remoteオプションで開始され、BytecodeVerificationLocalはfalseになります。
したがって、_need_verifyもfalseであり、assert_property(property、msg、index、CHECK)がcheck_propertyでパラメーターfalse、「クラスファイル%sのスーパークラスインデックス%uが無効です」、0、CHECK_NULLで呼び出されます。
inline void assert_property(bool b, const char* msg, int index, TRAPS) { #ifdef ASSERT if (!b) { ResourceMark rm(THREAD); fatal(err_msg(msg, index, _class_name->as_C_string())); } #endif }
実際、これはエラーメッセージがスローされる場所です。 致命的な(msg)を見て、これを行う方法を見つけてください。
ただし、すでに質問の一部に回答しています。 super_classフィールドが0に設定されているクラスファイルを作成し、デフォルトのClassLoaderを使用してロードすることはできません。
致命的はホットスポット/ src / share / vm / Utilities / debug.hppで定義されています:
#define fatal(msg) \ do { \ report_fatal(__FILE__, __LINE__, msg); \ BREAKPOINT; \ } while (0)
hotspot / src / share / vm / Utilities / debug.cpp:
void report_fatal(const char* file, int line, const char* message) { report_vm_error(file, line, "fatal error", message); } void report_vm_error(const char* file, int line, const char* error_msg, const char* detail_msg) { if (Debugging || error_is_suppressed(file, line)) return; Thread* const thread = ThreadLocalStorage::get_thread_slow(); VMError err(thread, file, line, error_msg, detail_msg); err.report_and_die(); }
hotspot / src / share / vm / Utilities / vmError.cppのreport_and_die()の実装は重要ではありませんが、Javaに戻っていないので、jvmの腸からエラーメッセージを表示します。 これについては、jvmとjava.lang.Objectの探索をやめたいと思います。
結論
java.lang.Objectは、スーパークラスとしてクラスが指定されていない一意のクラスファイルを持つ特別なクラスです。 Java言語を使用して同じクラスを作成することはできませんが、可能であれば、クラスファイルのバイトを操作してこれを行うことは困難です。 jvmソースの探索中に私が経験した賞賛の一部を伝えることができたと思います。 私は皆に同じことを試みるように促します。