02-设计概述

上一篇:01-导言


        本章重点讨论 JNI 中的主要设计问题。本节中的大多数设计问题都与本地方法有关。调用 API 的设计将在第 5 章:调用 API 中介绍。

2.1 JNI 接口函数和指针

        本地代码通过调用 JNI 函数来访问 Java 虚拟机功能。JNI 函数可通过接口指针使用。接口指针是指向指针的指针该指针指向一个指针数组,数组中每个指针指向一个接口函数每个接口函数都位于数组内预定义的偏移量处。下图 "接口指针 "说明了接口指针的组织结构。

        JNI 接口的组织方式类似于 C++ 虚拟函数表或 COM 接口。使用接口表而不是硬连线函数项的好处是,JNI 名称空间与本地代码分离。虚拟机可以轻松提供多个版本的 JNI 函数表。例如,虚拟机可以支持两个 JNI 函数表:

        ①. 一种会对非法参数进行彻底检查,适合调试;

        ②. 而另一种方法只执行 JNI 规范要求的最少检查量,因此效率更高。

        JNI 接口指针只在当前线程中有效。因此,本地方法不得将接口指针从一个线程传递到另一个线程。实现 JNI 的虚拟机可以在 JNI 接口指针指向的区域中分配和存储线程本地数据。

        本地方法接收 JNI 接口指针作为参数。当虚拟机从同一个 Java 线程多次调用本地方法时,会保证将同一个接口指针传递给本地方法。不过,本地方法可以从不同的 Java 线程调用,因此可能会收到不同的 JNI 接口指针

2.2 编译、加载和链接本地方法

        由于 Java 虚拟机是多线程的,因此本地库也应使用多线程感知本地编译器进行编译和链接。例如,使用 Sun Studio 编译器编译的 C++ 代码应使用 -mt 标志。对于使用 GNU gcc 编译器编译的代码,应使用标志 -D_REENTRANT 或 -D_POSIX_C_SOURCE 。

        本地方法用 System.loadLibrary 方法加载。在下面的示例中,类初始化方法加载了一个特定平台的本地库,其中定义了本地方法 f :

package p.q.r;

class A {
    native double f(int i, String s);
    static {
        // 
        // 
        /**
         * defined in "libcore/ojluni/src/main/java/java/lang/System.java"
         * public static void loadLibrary(String libname);
         * 加载由libname参数指定的本机库。
         * libname参数不能包含任何特定于平台的前缀、文件扩展名或路径。
         * 如果一个名为libname的本地库与虚拟机静态链接,则调用该库导出的JNI_OnLoad()函数。
         * 否则,libname参数将从系统库位置加载,并以依赖于实现的方式映射到本机库映像。
         */
        System.loadLibrary("p_q_r_A");
    }
}

        System.loadLibrary 的参数是程序员任意选择的库名。系统会采用一种标准的、但与特定平台有关的方法,将库名称转换为本地库名称。例如,Linux 系统会将库名 p_q_r_A 转换为 libp_q_r_A.so ,而 Windows 系统会将库名 p_q_r_A 转换为 p_q_r_A.dll

        程序员可以使用一个库来存储任意数量的类所需的所有本地方法,只要这些类是用同一个类加载器加载的虚拟机内部会为每个类加载器维护一个已加载的本地库列表。供应商在选择本地库名称时,应尽量减少名称冲突的机会。

        对动态链接和静态链接库的支持,以及它们各自的生命周期管理 "加载 "和 "卸载 "功能钩子,在库和版本管理的调用 API 部分有详细介绍。

2.2.1 解析本地方法名称

        JNI 定义了从 Java 中声明的 native 方法名称到本地库中本地方法名称的 1:1 映射。虚拟机使用该映射将 native 方法的 Java 调用动态链接到本地库中的相应实现。

        该映射通过连接从 native 方法声明中派生出来的以下组件来生成本地方法名称:

                ①. Java_前缀;

                ②. 给出了声明 native 方法的类的二进制内部名称转义该名称的结果

                ③.  ("_") 下划线

                ④. 转义方法名

                ⑤. 如果 native 方法声明是重载的:两个下划线("__"),后跟方法声明的转义参数描述符(JVMS 4.3.3)。

        转义时,每个字母数字 ASCII 字符 ( A-Za-z0-9 ) 都保持不变,并用相应的转义序列替换下表中的每个 UTF-16 代码单元。如果要转义的名称包含一对代用字符,则高代用代码单元和低代用代码单元将分别转义。转义的结果是一个仅由 ASCII 字符 A-Za-z0-9 和下划线组成的字符串。

        出于两个原因,转义是必要的。首先,为了确保 Java 源代码中可能包含 Unicode 字符的类名和方法名能转换成 C 源代码中有效的函数名。其次,确保 native 方法的参数描述符(使用"; "和"["字符对参数类型进行编码)可以在 C 函数名中编码

        当 Java 程序调用一个 native 方法时,虚拟机会搜索本地库,首先查找本地方法名称的简短版本,即不包含转义参数签名的名称如果找不到短名称的本地方法,虚拟机就会查找长版本的本地方法名称,即包含转义参数签名的名称

        先查找简短的名称,可以更方便地在本地库中声明实现。例如,给出 Java 中的 native 方法:

package p.q.r;
class A {
    native double f(int i, String s);
}

        相应的 C 函数可以命名为 Java_p_q_r_A_f ,而不是 Java_p_q_r_A_f__ILjava_lang_String_2 。

        只有当一个类中的两个或多个 native 方法具有相同的名称时,才有必要在本地库中声明具有长名称的实现。例如,Java 中有以下1 个方法:

package p.q.r;
class A {
    native double f(int i, String s);
    native double f(int i, Object s);
}

        相应的 C 语言函数必须命名为 Java_p_q_r_A_f__ILjava_lang_String_2 和 Java_p_q_r_A_f__ILjava_lang_Object_2 ,因为这两个方法是重载的。

        如果 Java 中的 native 方法只被非 native 方法重载,则无需在本地库中使用长名称。在下面的示例中, native 方法 g 不必使用长名称链接,因为另一个方法 g 不是 native 方法,因此不存在于本地库中。

package p.q.r;
class B {
    int g(int i);
    native int g(double d);
}

        请注意,转义序列可以安全地以 _0 、 _1 等开头,因为 Java 源代码中的类名和方法名从不以数字开头。但是,在非 Java 源代码生成的类文件中,情况并非如此。为了保持与本地方法名称的 1:1 映射,虚拟机会对生成的名称进行如下检查。如果从方法声明(类或方法名称或参数类型)中转义任何前导字符串的过程导致前导字符串中的 " 0 "、" 1 "、" 2 "或 " 3 "字符在结果中紧跟下划线后或在转义字符串的开头(在完全组装后的名称中,这些字符将紧跟下划线)出现,且未发生变化,则称转义过程 "失败"。在这种情况下,将不执行本地库搜索,并且在尝试链接 native 方法调用时将抛出 UnsatisfiedLinkError 。可以扩展目前的简单映射方案以涵盖这种情况,但复杂性成本将超过任何好处。

        本地方法和接口 API 都遵循特定平台上的标准库调用约定。例如,UNIX 系统使用 C 调用约定,而 Win32 系统使用 __stdcall。

        本机方法也可以使用 RegisterNatives 功能进行显式链接需要注意的是, RegisterNatives 函数可以通过更改给定本地 Java 方法要执行的本地代码来改变 JVM 的记录行为(包括加密算法、正确性、安全性、类型安全性)。因此,请谨慎使用使用 RegisterNatives 函数的本地库应用程序。

2.2.2 本地方法参数

        JNI 接口指针是本地方法的第一个参数。JNI 接口指针的类型是 JNIEnv。第二个参数根据本地方法是静态还是非静态而有所不同。非静态本地方法的第二个参数是对象的引用静态本地方法的第二个参数是对其 Java 类的引用

        其余参数与常规 Java 方法参数相对应。本地方法调用通过返回值将结果传回调用例程。第 3 章:JNI 类型和数据结构,介绍了 Java 和 C 语言类型之间的映射。

        下面的代码示例说明了如何使用 C 语言函数来实现本地方法 f 。本地方法 f 的声明如下:

package p.q.r;

class A {
    native double f(int i, String s);
    // ...
}

        长名称为 Java_p_q_r_A_f_ILjava_lang_String_2 的 C 语言函数实现了本地方法 f :

jdouble Java_p_q_r_A_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);

     /* process the string */
     ...

     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);

     return ...
}

        请注意,我们总是使用接口指针 env 来操作 Java 对象。如下面的代码示例所示,使用 C++ 可以编写稍微简洁的代码:

extern "C" /* specify the C calling convention */

jdouble Java_p_q_r_A_f__ILjava_lang_String_2 (

     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */

{
     const char *str = env->GetStringUTFChars(s, 0);

     // ...

     env->ReleaseStringUTFChars(s, str);

     // return ...
}

        在 C++ 中,额外的间接层次和接口指针参数从源代码中消失了。在 C++ 中,JNI 函数被定义为内联成员函数,可扩展为 C 语言的对应函数

2.3 引用 Java 对象

        原始类型(如整数、字符等)可在 Java 和本地代码之间复制而任意 Java 对象则通过引用传递虚拟机必须跟踪所有已传递给本地代码的对象,以免垃圾回收器释放这些对象。反过来,本地代码也必须有办法通知虚拟机它不再需要这些对象。此外,垃圾回收器必须能够移动本地代码引用的对象。

2.3.1 全局和本地引用

        JNI 将本地代码使用的对象引用分为两类:本地引用和全局引用。本地引用在本地方法调用期间有效,并在本地方法返回后自动释放。全局引用在显式释放之前一直有效

        对象以本地引用的形式传递给本地方法。JNI 函数返回的所有 Java 对象都是局部引用。JNI 允许程序员从局部引用创建全局引用。期望返回 Java 对象的 JNI 函数既接受全局引用,也接受局部引用本地方法可将本地引用或全局引用作为结果返回给 VM

        在大多数情况下,程序员应依靠虚拟机在本地方法返回后释放所有本地引用。不过,有时程序员应该显式释放本地引用。例如,请考虑以下情况:

                ①. 本地方法访问大型 Java 对象,从而创建 Java 对象的本地引用。然后,本地方法在返回给调用者之前会执行额外的计算。即使在剩余的计算中不再使用该对象,该大型 Java 对象的本地引用也会阻止该对象被垃圾回收

                ②. 本地方法会创建大量的本地引用,但并非所有引用都会同时使用。由于虚拟机需要一定的空间来跟踪本地引用,创建过多的本地引用可能会导致系统内存不足。例如,一个本地方法在一个大型对象数组中循环,以本地引用的形式检索元素,每次迭代对一个元素进行操作。每次迭代后,程序员就不再需要数组元素的本地引用了。

        JNI 允许程序员在本地方法的任意位置手动删除本地引用。为确保程序员能手动释放本地引用,JNI 函数不允许创建额外的本地引用,但作为结果返回的引用除外。

        本地引用只在创建引用的线程中有效。本地代码不得将本地引用从一个线程传递到另一个线程

2.3.2 本地引用的实现

        为了实现本地引用,Java 虚拟机会为每次从 Java 到本地方法的控制转换创建一个注册表。注册表将不可移动的本地引用映射到 Java 对象,并防止对象被垃圾回收。传递给本地方法的所有 Java 对象(包括作为 JNI 函数调用结果返回的对象)都会自动添加到注册表中。本地方法返回后,注册表将被删除,从而允许对其所有条目进行垃圾回收。

        实现注册表的方法有很多种,例如:使用表、链表或哈希表。虽然可以使用引用计数来避免注册表中的重复条目,但 JNI 实现没有义务检测和删除重复条目

        请注意,本地引用不能通过保守地扫描本地堆栈来忠实地实现。本地代码可能会将本地引用存储到全局或堆数据结构中。

2.4 访问 Java 对象

        JNI 为全局和局部引用提供了丰富的访问函数集。这意味着,无论虚拟机如何在内部表示 Java 对象,都能使用相同的本地方法实现。这也是 JNI 可以被各种虚拟机实现所支持的重要原因。

        通过不透明引用使用访问函数的开销要高于直接访问 C 数据结构的开销。我们相信,在大多数情况下,Java 程序员会使用本地方法来执行一些非繁琐的任务,而这些任务会掩盖该接口的开销。

2.4.1 访问原始数组

        对于包含许多原始数据类型(如整数数组和字符串)的大型 Java 对象来说,这种开销是不可接受的。(考虑一下用于执行向量和矩阵计算的本地方法)。通过函数调用遍历 Java 数组并检索每个元素的效率非常低。

        一种解决方案引入了 "锁定 "的概念,这样本地方法就可以要求虚拟机锁定数组的内容。然后,本地方法会收到指向元素的直接指针。不过,这种方法有两个影响:

                ①. 垃圾回收器必须支持“锁住”功能。

                ②. 虚拟机必须在内存中连续布局基元数组。虽然这对大多数基元数组来说是最自然的实现方式,但布尔数组可以以打包或未打包的方式实现。因此,依赖于布尔数组精确布局的本地代码将无法移植。

        我们采取了一种折中的方法来克服上述两个问题。

        首先,我们提供了一组函数,用于在 Java 数组段和本地内存缓冲区之间复制原始数组元素。如果本地方法只需访问大型数组中的少量元素,则使用这些函数。

        其次,程序员可以使用另一组函数来检索数组元素的固定版本。请记住,这些函数可能需要 Java 虚拟机执行存储分配和复制。这些函数实际上是否复制数组取决于 VM 的实现,具体如下:

                ①. 如果垃圾回收器支持"锁定",且数组布局与本地方法预期的相同,则无需复制。

                ②. 否则,数组将被复制到一个不可移动的内存块(例如,在 C 堆中),并执行必要的格式转换。系统将返回指向拷贝的指针

        最后,接口提供了一些函数,用于通知虚拟机本地代码不再需要访问数组元素。调用这些函数时,系统要么取消数组的锁定,要么将原始数组与其不可移动的副本进行核对,并释放副本

        我们的方法具有灵活性。垃圾回收器算法可以针对每个给定的数组分别做出复制或“锁定”的决定。例如,垃圾回收器可以复制小对象,但“锁定”较大的对象。

        JNI 实现必须确保在多个线程中运行的本地方法可以同时访问同一个数组。例如,JNI 可以为每个被钉住的数组保留一个内部计数器,这样一个线程就不会解除被另一个线程钉住的数组。请注意,JNI 不需要为本地方法的独占访问锁定原始数组不同线程同时更新 Java 数组会导致非确定性结果

2.4.2 访问字段和方法

        JNI 允许本地代码访问 Java 对象的字段和调用 Java 对象的方法。JNI 通过符号名称和类型签名来识别方法和字段。根据字段或方法的名称和签名,分两步计算出查找字段或方法的成本。例如,要调用类 cls 中的方法 f ,本地代码首先要获取一个方法 ID,如下所示:

jmethodID mid = env->GetMethodID(cls, "f", "(ILjava/lang/String;)D");

        这样,本地代码就可以重复使用该方法 ID,而无需花费方法查找的成本,如下所示:

jdouble result = env->CallDoubleMethod(obj, mid, 10, str);

        字段或方法 ID 不会阻止虚拟机卸销毁 ID 所派生的类。类销毁后,方法或字段 ID 将失效,并且不能传递给任何使用该 ID 的函数。因此,本地代码如果打算长期使用某个方法或字段 ID,必须确保:

                ①. 保持对底层类的实时引用,或

                ②. 重新计算方法或字段 ID;

        JNI 对内部如何实现字段和方法 ID 没有施加任何限制。

2.4.2.1 调用对调用者敏感的方法

        少数 Java 方法具有一种特殊属性,称为调用者敏感性。对调用者敏感的方法可以根据直接调用者的身份做出不同的行为。例如,AccessibleObject::canAccess 需要知道调用者才能确定是否可访问。

        当本地代码调用此类方法时,调用栈上可能没有任何 Java 调用者。程序员有责任了解从本地代码调用的 Java 方法是否对调用者敏感,以及如果没有 Java 调用者,这些方法将如何响应。如果有必要,程序员可以提供 Java 代码供本地代码调用,然后本地代码再调用原始 Java 方法

2.5 报告编程错误

        JNI 不会检查编程错误,如:传递 NULL 指针或非法参数类型。非法参数类型包括:使用普通 Java 对象而非 Java 类对象等。JNI 不检查这些编程错误的原因如下:

                ①. 强制 JNI 函数检查所有可能的错误条件会降低正常(正确)本地方法的性能。

                ②. 在许多情况下,没有足够的运行时类型信息来执行这种检查。

        多数 C 库函数都不会防范编程错误。例如, printf() 函数在接收到无效地址时通常会导致运行时错误,而不是返回错误代码。强制 C 库函数检查所有可能的错误条件很可能会导致重复检查:在用户代码中检查一次,然后在库中再检查一次

        程序员不得向 JNI 函数传递非法指针或错误类型的参数。否则可能导致任意后果,包括系统状态损坏或虚拟机崩溃。

也就是说:传递这种空指针异常(nullptr)的检查,由用户负责;

2.6 Java异常

        JNI 允许本地方法引发任意 Java 异常。本地代码也可以处理未处理的 Java 异常。未处理的 Java 异常会传播回虚拟机

2.6.1 异常和错误代码

        某些 JNI 函数使用 Java 异常机制来报告错误条件。在大多数情况下,JNI 函数通过返回错误代码和抛出 Java 异常来报告错误条件。错误代码通常是超出正常返回值范围的特殊返回值(如 NULL)。因此,程序员可以:

                ①. 快速检查最后一次 JNI 调用的返回值,以确定是否发生错误,以及

                ②. 调用 ExceptionOccurred() 函数来获取异常对象,该对象包含对错误条件的更详细描述。

        在两种情况下,程序员需要检查异常,而无法首先检查错误代码:

                ①. 调用 Java 方法的 JNI 函数会返回 Java 方法的结果。程序员必须调用 ExceptionOccurred() 来检查 Java 方法执行过程中可能出现的异常。

                ②. 某些 JNI 数组访问函数不会返回错误代码,但可能会抛出 ArrayIndexOutOfBoundsException ArrayStoreException

        在所有其他情况下,非错误返回值保证没有抛出异常。

2.6.2 异步异常

        一个线程可以通过调用 Thread.stop() 方法在另一个线程中引发异步异常,该方法自 Java 2 SDK 1.2 版起已被弃用。强烈建议程序员不要使用 Thread.stop() ,因为它通常会导致应用程序状态不确定

        此外,JVM 可能会在当前线程中产生异常,但这些异常并不是 JNI API 调用的直接结果,而是因为 JVM 内部的各种错误,例如: 像 StackOverflowError OutOfMemoryError VirtualMachineError 。这些异常也被称为异步异常。

        异步异常不会立即影响当前线程中本地代码的执行,直到出现以下情况:

                ①. 本地代码调用了一个可能引发同步异常的 JNI 函数,或

                ②. 本地代码使用 ExceptionOccurred() 来明确检查同步和异步异常。

        请注意,只有那些可能引发同步异常的 JNI 函数才会检查异步异常。

        本地方法应在必要的地方插入 ExceptionOccurred() 检查,例如:在没有其他异常检查的任何长时间运行代码中(可能包括紧密循环)。这样可以确保当前线程在合理的时间内响应异步异常。不过,由于异步异常的特性,在调用前进行异常检查并不能保证在检查和调用之间不会引发异步异常。

2.6.3 异常处理

        在本地代码中有两种处理异常的方法:

                ①. 本机方法可以选择立即返回,从而在启动本机方法调用的 Java 代码中抛出异常。

                ②. 本地代码可以通过调用 ExceptionClear() 清除异常,然后执行自己的异常处理代码。

        出现异常后,本地代码必须先清除异常,然后才能调用其他 JNI 函数。当出现待处理异常时,可以安全调用的 JNI 函数有:

ExceptionOccurred()
ExceptionDescribe()
ExceptionClear()
ExceptionCheck()
ReleaseStringChars()
ReleaseStringUTFChars()
ReleaseStringCritical()
Release<Type>ArrayElements()
ReleasePrimitiveArrayCritical()
DeleteLocalRef()
DeleteGlobalRef()
DeleteWeakGlobalRef()
MonitorExit()
PushLocalFrame()
PopLocalFrame()
DetachCurrentThread()

下一篇: 03-JNI 类型和数据结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/425894.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式(九)模版方法模式

请直接看原文:设计模式&#xff08;九&#xff09;模版方法模式_模板方法模式的优缺点-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 1.模版方法模式简介 模版方法模式介…

无人机巡检技术方案,无人机智能化巡检在火电厂的应用场景分析

无人机智能化巡检是一种将先进的无人机技术与人工智能、大数据分析等现代信息技术相结合的新型巡检方式&#xff0c;主要用于替代或辅助传统的人工巡检&#xff0c;在多个领域实现高效、精准和安全的巡查工作。 无人机技术在火电厂巡检中的应用&#xff1a; 无人机电力巡航&a…

中科大计网学习记录笔记(十七):拥塞控制原理 | TCP 拥塞控制

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…

(每日持续更新)jdk api之PipedReader基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

多路转接之epoll

常用的三个API&#xff1a; epoll_create(); //例如 int epfd epoll(10);创建一棵有10个结点的红黑树&#xff0c;注意&#xff1a;这个数只是对内核建议的数值&#xff0c;内核参照这个参数去构建epoll_ctrl();//参数2 op可以取值 EPOLL_CTL_ADD/MOD/DELevents:EPOLLIN/…

C++进阶(二) 多态

一、多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c; 具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会 产生出不同的状态。举个栗子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&#xff1b;学…

Sora:探索大型视觉模型的前世今生、技术内核及未来趋势

Sora&#xff0c;一款由OpenAI在2024年2月推出的创新性文生视频的生成式AI模型&#xff0c;能够依据文字说明&#xff0c;创作出既真实又富有想象力的场景视频&#xff0c;展现了其在模拟现实世界方面的巨大潜能。本文基于公开技术文档和逆向工程分析&#xff0c;全面审视了Sor…

java八股文复习-----2024/03/03

1.接口和抽象类的区别 相似点&#xff1a; &#xff08;1&#xff09;接口和抽象类都不能被实例化 &#xff08;2&#xff09;实现接口或继承抽象类的普通子类都必须实现这些抽象方法 不同点&#xff1a; &#xff08;1&#xff09;抽象类可以包含普通方法和代码块&#x…

work 3/1

1>机械臂 #include <head.h> #define SER_POTR 8899 #define SER_IP "192.168.125.223" int main(int argc, const char *argv[]) {//创建套接字int cfdsocket(AF_INET,SOCK_STREAM,0);if(cfd-1){perror("");return -1;}//链接struct sockaddr_i…

腾讯云4核8G服务器申请费用多少?性能如何?支持几个人?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

【详识JAVA语言】面向对象程序三大特性之三:多态

多态 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态实现条件 在java中要实现多态&#xff0c;必须要满足如下几个条件&#xff0c;缺一不可&#xf…

Sqli-labs靶场第20关详解[Sqli-labs-less-20]自动化注入-SQLmap工具注入

Sqli-labs-Less-20 通过测试发现&#xff0c;在登录界面没有注入点&#xff0c;通过已知账号密码admin&#xff0c;admin进行登录发现&#xff1a; 登录后会有记录 Cookie 值 设想如果在Cookie尝试加上注入语句&#xff08;报错注入&#xff09;&#xff0c;测试是否会执行…

Hololens2开发环境配置及项目生成部署

Hololens2开发环境配置及项目生成部署 Hololens2开发环境配置及项目生成部署一、官方文档及推荐配置说明1.官方文档介绍2.推荐配置及配置说明 二、安装步骤0.现有Visual Stuido和Unity卸载1.Windows SDK安装2.Visual Studio安装3.Unity安装4.MRTK配置 三、初次环境配置1.新建Un…

C++进阶(三) 二叉搜索树

一、二叉搜索树 1.1 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节…

Postman上传文件的操作方法

前言 调用某个接口&#xff0c;测试上传文件功能。一时间不知如何上传文件&#xff0c;本文做个操作记录&#xff0c;期望与你有益。 步骤一、设置Headers key:Content-Type value:multipart/form-data 步骤二、设置Body 选择form-data key:file下拉框选择file类型value&…

2024年【道路运输企业主要负责人】考试报名及道路运输企业主要负责人模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业主要负责人考试报名根据新道路运输企业主要负责人考试大纲要求&#xff0c;安全生产模拟考试一点通将道路运输企业主要负责人模拟考试试题进行汇编&#xff0c;组成一套道路运输企业主要负责人全真模拟考…

Mysql学习之各种锁

锁 事务的隔离性由锁来实现 MySQL并发事务访问相同记录 并发事务访问相同记录的情况大致可以分为3种&#xff1a; 读-读的情况 读-读情况&#xff0c;即并发事务相继读取相同的记录。读取操作本身不会对记录由有任何的影响&#xff0c;并不会引起什么问题&#xff0c;所以允许…

【SQL注入】宽字节注入原理讲解

一、addslasehes()转义函数 addslashes() 是 PHP 中用于转义字符串中的特殊字符的函数之一。它会在指定的预定义字符&#xff08;单引号、双引号、反斜线和 NUL 字符&#xff09;前面添加反斜杠&#xff0c;以防止这些字符被误解为代码注入或其他意外操作。 1. 用法 string …

对程序、进程、线程、并发、并行、高并发概念的讲解

一、概述 程序、进程、线程、并发、并行和高并发是计算机科学领域中非常重要的概念。 了解进程、线程、并发和并行的概念&#xff0c;可以更好地利用计算机的多核处理器和并行计算能力&#xff0c;提高计算机性能。 了解进程和线程为操作系统中的资源管理提供了基础&#xff…

Springboot+vue的考勤管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的考勤管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…