Java プログラムの実行に関する基本的な知識
関数実行までの JVM スレッド スタック
すべての JVM スレッドは、開始時にプライベート スレッド スタックを作成します。 jvm スレッド スタックは、スタック フレームを格納するために使用されます。jvm スレッド スタックは、C 言語のスタックと非常によく似ており、ローカル変数と一部の演算結果の管理を担当し、関数呼び出しと関数の戻りにも関与します。 JVM 仕様の実行スレッド スタックのサイズは、固定することも動的に割り当てることも、特定のルールに従って計算することもできます。スタックの実装は jvm ごとに異なります。開発者に jvm スレッド スタックの初期サイズを制御する方法を提供するものや、jvm の最大値と最小値の設定を提供するものもあります。
スレッドに必要な割り当てサイズの計算が固定値または設定された最大値を超えると、jvm は StackOverflowError をスローします。動的に割り当てられたスタックの場合、メモリが最小値または必要な値を満たすのに十分なスペースを提供できない場合、JVM は OutOfMemoryError
スタック フレームをスローします。これは、パラメーターとローカル変数を管理する関数実行環境として理解できます。 、戻り値など。
各スタックフレームにはローカル変数(ローカル変数)を管理する配列が含まれており、この配列のユニット数はバイトコードにコンパイルされるときに決定されます。 32 ビットの場合、1 つのユニットに boolean、byte、char、short、int、float、reference、returnAddress を格納でき、連続する 2 つのユニットを使用して long および double を格納できます。ローカル変数配列の添字は 0 から始まります。一般的に、0 の位置にこれが格納され、その後に関数のパラメータが格納され、その後に関数に現れるローカル変数が格納されます。
各スタック フレームには、(LIFO) 演算スタック データ構造 (オペランド スタック) も含まれます。そのサイズは、作成時に空のスタックになります。 int a+b の一般的な使用法を説明する簡単な例を挙げてみましょう。まず a をスタックにプッシュし、次に b をスタックにプッシュし、次に 2 つの値を同時にポップして iadd 命令を実行します。そして結果をスタックにプッシュして命令を完了します。
上記の 2 つの主要な構造に加えて、各スタック フレームには定数プール (実行時定数プール)、例外スロー管理、その他の構造もあります。ここでは詳しく説明しませんので、他の情報を参照してください。
簡単なデモを使用して、スタック フレームの動作を説明しましょう。まず、そのような関数を見てみましょう:
public int comp(float number1, float number2){ int result ; if(number1 < number2) result = 1; else result = 2; return result; }
関数内のロジックに対応するバイトコードは次のとおりです:
0: fload_1
1: fload_2
2: fcmpg
3: ifge 11
6: iconst_1
7: istore_3
8: goto 13
11: iconst_2
12: istore_3
13: iload_3
14: ireturn
これらのバイトコード命令 fload について簡単に説明します。 _x: x 番目の変数を取得しますローカル変数配列、load と入力し、プッシュしてスタックにプッシュします。 fcmpg: 2 つの単精度浮動小数点数を比較します。 2 つの数値が 1 より大きい場合、結果は 0 となり、等しい場合、結果は -1 になります。 iconst_x: スタックに定数 x をプッシュします。ローカル変数配列 x 番目; iload_x: x 番目のローカル変数配列を読み取り、スタックにプッシュします。 よく見ると、それがわかります。 i の先頭は int を指し、f の先頭はロードを指します。load はロードを表し、if はジャンプを表します。バイトコードのオペレーション コード定義にも特定の意味があります。詳細は、jvm バイトコード関連に変換できます。規格。特定の呼び出し comp(1.02,2.02) を例として、スタック フレーム構造上で jvm がどのように実行されるかを見てみましょう:Java のクラス
バイトコードに関して言えば、.class は不可欠です。クラスの内容を詳しく見るには、デモ クラスを受講するとよいでしょう。クラスは 2 つの関数のうちの 1 つが非常に単純で、もう 1 つは上記の cmp 関数です。
public class Hello { public void say(){ System.out.println("Hello world!"); } public int comp(float number1, float number2){ int result ; if(number1 < number2) result = 1; else result = 2; return result; } }
javac -g:none Hello.java を使用してこのクラスをコンパイルし、次に javap -c -v Hello.class を使用してコンパイルされたクラスを解析します。
Classfile /src/main/java/com/demo/Hello.class Last modified 2016-10-28; size 404 bytes MD5 checksum 9ac6c800c312d65b568dd2a0718bd2c5public class com.demo.Hello minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#14 // java/lang/Object."<init>":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello world! #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #20 // com/demo/Hello #6 = Class #21 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 say #11 = Utf8 comp #12 = Utf8 (FF)I #13 = Utf8 StackMapTable #14 = NameAndType #7:#8 // "<init>":()V #15 = Class #22 // java/lang/System #16 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #17 = Utf8 Hello world! #18 = Class #25 // java/io/PrintStream #19 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #20 = Utf8 com/demo/Hello #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V{ public com.demo.Hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public void say(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello world! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return public int comp(float, float); descriptor: (FF)I flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=3 0: fload_1 1: fload_2 2: fcmpg 3: ifge 11 6: iconst_1 7: istore_3 8: goto 13 11: iconst_2 12: istore_3 13: iload_3 14: ireturn StackMapTable: number_of_entries = 2 frame_type = 11 /* same */ frame_type = 252 /* append */ offset_delta = 1 locals = [ int ] }
getstatic:获取镜头变量; invokevirtual:调用函数; return:void 函数结束返回;
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
minor_version 、major_version :指的是java class 文件的版本,一般说class文件的版本是 XX.xx 其中XX 就是major,xx是minor,比如上面demo中的版本是52.0 代表就是 minor 0,major 51.
constant_pool_count:就是常量池元素个数,cp_info constant_pool[constant_pool_count-1] 就是相关的详细信息了。
access_flags:指的是访问标识例如ACC_PUBLIC、ACC_FINAL、ACC_INTERFACE、ACC_SUPER 写过java的相信看名字应该知道啥意思,ACC是access的缩写。
其他具体的,就不一一介绍了详细可以直接参考官方文档。
动态生成java字节码
当然,你可以直接按照官方的class文件格式来直接写 byte[],然后自定义个 class load 载入编写的byte[]来实现动态生成class。不过,这个要求可能也有点高,必须的非常熟悉class文件格式才能做到。这里demo还是借助 ASM 这个类库来简单演示下,就编写下 上面的Hello 不过里面只实现say的方法。如下:
public class AsmDemo { public static final String CLASS_NAME = "Hello"; public static final AsmDemoLoad load = new AsmDemoLoad(); private static class AsmDemoLoad extends ClassLoader { public AsmDemoLoad() { super(AsmDemo.class.getClassLoader()); } public Class<?> defineClassForName(String name, byte[] data) { return this.defineClass(name, data, 0, data.length); } } public static byte[] generateSayHello() throws IOException { ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER, CLASS_NAME, null, getInternalName(Object.class), null); //默认初始化函数 Method constructorMethod = Method.getMethod("void <init> ()"); GeneratorAdapter constructor = new GeneratorAdapter(ACC_PUBLIC, constructorMethod, null, null, classWriter); constructor.loadThis(); //每个类都要基础Object constructor.invokeConstructor(Type.getType(Object.class), constructorMethod); constructor.returnValue(); constructor.endMethod(); Method mainMethod = Method.getMethod("void say ()"); GeneratorAdapter main = new GeneratorAdapter(ACC_PUBLIC, mainMethod, null, null, classWriter); main.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); main.push("Hello world!"); main.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); main.returnValue(); main.endMethod(); return classWriter.toByteArray(); } public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException, IOException { byte[] code = AsmDemo.generateSayHello(); //反射构建 hello 类,调用hello方法。 Class<?> hello = load.defineClassForName(CLASS_NAME, code); hello.getMethod("say", null).invoke(hello.newInstance(), null); } }
关于动态生成字节码用途,一定场景下是可以提升效率与性能,因为动态生成的类和普通的载入类并无太大区别。手工优化后的字节码执行可能比编译的要优,可以替代反射使用的许多场景 同时避免反射的性能消耗。很著名的一个例子,fastJSON 就是使用内嵌 ASM 框架动态生成字节码类,来进行序列和反序列化工作,是目前公认最快的json字符串解析。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。
