每日学习Java之一万个为什么

发布于:2025-03-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

JVM的加载过程

  • 启动阶段:启动JVM实例,设置初始配置参数,加载核心类库如java.lang
  • 类加载器:自举加载器,扩展加载器,系统加载器,自定义加载器。分别负责- 1.核心类库rt.jar等 2.扩展目录下的类库ext目录 3.应用类路径下的类库 classpath
  • 类加载:加载二进制数据存储方法区,检查安全性,准备分配内存并设置初始默认值,解析常量池中的符号引用转换为直接引用,执行静态变量赋值和静态代码块,完成类的初始化工作。
  • 字节码执行:解释执行,即时编译,垃圾回收
graph TD;
    A[启动JVM] --> B[初始化JVM];
    B --> C[加载核心类库];
    C --> D[类加载器];
    D --> E[Bootstrap ClassLoader];
    D --> F[Extension ClassLoader];
    D --> G[System ClassLoader];
    D --> H[自定义类加载器];
    E --> I["加载核心类 (rt.jar, etc.)"];
    F --> J["加载扩展类 (ext目录)"];
    G --> K["加载应用类 (classpath)"];
    H --> L["加载自定义类"];
    I --> M[验证];
    J --> M;
    K --> M;
    L --> M;
    M --> N[准备];
    N --> O[解析];
    O --> P[初始化];
    P --> Q[字节码执行];
    Q --> R[解释执行];
    Q --> S[JIT 编译];
    Q --> T[垃圾回收];

    subgraph 初始化JVM
        B1[设置系统属性]
        B2[加载安全管理器]
        B3[初始化内存管理]
        B4[初始化线程调度]
        B5[初始化信号处理]
        B6[初始化JNI接口]
        B --> B1 & B2 & B3 & B4 & B5 & B6
    end

    subgraph 加载核心类库
        C1[加载 rt.jar]
        C2[加载其他核心库]
        C --> C1 & C2
    end

    subgraph 类加载器
        E1[加载 java.lang.Object 等核心类]
        F1[加载 javax.swing 等扩展类]
        G1[加载用户应用程序类]
        H1[加载特定位置或方式的类]
        E --> E1
        F --> F1
        G --> G1
        H --> H1
    end

    subgraph 验证
        M1[文件格式验证]
        M2[元数据验证]
        M3[字节码验证]
        M4[符号引用验证]
        M --> M1 & M2 & M3 & M4
    end

    subgraph 准备
        N1[为静态变量分配内存]
        N2[设置默认初始值]
        N --> N1 & N2
    end

    subgraph 解析
        O1[将常量池中的符号引用转换为直接引用]
        O --> O1
    end

    subgraph 初始化
        P1[执行静态变量赋值]
        P2[执行静态代码块]
        P --> P1 & P2
    end

    subgraph 字节码执行
        R1[逐条解释执行字节码指令]
        S1[将热点代码编译为本地机器码]
        T1[识别并回收不再使用的对象]
        Q --> R1 & S1 & T1
    end

Java中的本地方法native

在Java中,本地方法是一种机制,允许java代码调用非java代码。这些代码被编译成共享库如Windows上的DDL文件,通过JNI java native interface 与JVM虚拟机交互。

通过这种方式,java程序可以利用底层操作系统的功能,提高性能和访问特定硬件资源。

本地方法实现:

  • native声明
public class MyClass {
    public native void myNativeMethod();
}
  • 头文件生成
javac -h . MyClass.java
  • 使用System.loadLibrary(“libName”)加载共享库
  • 实现本地方法并通过JNI与JVM通信
#include "MyClass.h"
#include <jni.h>

JNIEXPORT void JNICALL Java_MyClass_myNativeMethod(JNIEnv *env, jobject obj) {
    // C/C++ 实现
}


gcc -shared -o libmylib.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux MyClass.c



public class Main {
    static {
        System.loadLibrary("mylib");
    }

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.myNativeMethod();
    }
}

Java中的常见native方法

  • System类中的arraycopy currenTimeMillis
// 声明本地方法
public native static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
public native static long currentTimeMillis();

// 实现本地方法
JNIEXPORT void JNICALL Java_java_lang_System_arraycopy(JNIEnv *env, jclass cls, jobject src, jint srcPos, jobject dest, jint destPos, jint length) {
    // C/C++ 实现
}

JNIEXPORT jlong JNICALL Java_java_lang_System_currentTimeMillis(JNIEnv *env, jclass cls) {
    // C/C++ 实现
}
  • java.io.FileInputStream 中的 readBytes
// 声明本地方法
private native int readBytes(byte b[], int off, int len) throws IOException;

// 实现本地方法
JNIEXPORT jint JNICALL Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject thisObj, jbyteArray bytes, jint offset, jint length) {
    // C/C++ 实现
}
  • java.util.zip.ZipFile :open 和 getNextEntry
// 声明本地方法
private native long open(String name, boolean shared, long lastModified, FileDescriptor fdobj) throws IOException;
private native ZipEntry getNextEntry(long jzfile);

// 实现本地方法
JNIEXPORT jlong JNICALL Java_java_util_zip_ZipFile_open(JNIEnv *env, jobject zipFileObj, jstring name, jboolean shared, jlong lastModified, jobject fdObj) {
    // C/C++ 实现
}

JNIEXPORT jobject JNICALL Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jobject zipFileObj, jlong jzfile) {
    // C/C++ 实现
}

编程中的Entry出处

entry有两个意思,代码中的entry通常就有两类方法,一个指的是入口。
例如汇编程序中的伪指令 ENTRY 指的是程序入口
一个指的是条目例如键值对,数据行。

Java中的Entry 接口

参考文章

为什么流的toString 有 $ 号

常规toString,Object类下的实现

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

在Java中,$ 符号出现在类名中通常是因为该类是内部类或辅助类。具体到ReferencePipeline$Head,这里的 $用于区分外部类和内部类的名字。ReferencePipeline是一个实现流(Stream)操作的基础抽象类,而Head是它的一个静态内部类。

ReferencePipeline.Head代表了流的头部或者说是起始点,它是流的第一个处理阶段,负责从数据源获取元素,并开始一系列中间操作和终端操作。使用内部类可以帮助组织代码,使代码更加模块化和清晰,同时也有助于封装实现细节,避免不必要的暴露。

在Java编译器生成的字节码文件中,内部类的名字通常会包含一个 $符号,以示区分。例如,如果有一个名为OuterClass的外部类和一个名为InnerClass的内部类,那么编译后的字节码文件可能会被命名为OuterClass $InnerClass.class。这是Java语言规范的一部分,用以确保不同类之间的名字空间隔离。因此,在查看类名或调试时看到 $符号是正常的,它指示了一个内部类的存在。

参考文章

静态内部类Head源码:

   static class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> {
        /**
         * Constructor for the source stage of a Stream.
         *
         * @param source {@code Supplier<Spliterator>} describing the stream
         *               source
         * @param sourceFlags the source flags for the stream source, described
         *                    in {@link StreamOpFlag}
         */
        Head(Supplier<? extends Spliterator<?>> source,
             int sourceFlags, boolean parallel) {
            super(source, sourceFlags, parallel);
        }

        /**
         * Constructor for the source stage of a Stream.
         *
         * @param source {@code Spliterator} describing the stream source
         * @param sourceFlags the source flags for the stream source, described
         *                    in {@link StreamOpFlag}
         */
        Head(Spliterator<?> source,
             int sourceFlags, boolean parallel) {
            super(source, sourceFlags, parallel);
        }

        @Override
        final boolean opIsStateful() {
            throw new UnsupportedOperationException();
        }

        @Override
        final Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink) {
            throw new UnsupportedOperationException();
        }

        // Optimized sequential terminal operations for the head of the pipeline

        @Override
        public void forEach(Consumer<? super E_OUT> action) {
            if (!isParallel()) {
                sourceStageSpliterator().forEachRemaining(action);
            }
            else {
                super.forEach(action);
            }
        }

        @Override
        public void forEachOrdered(Consumer<? super E_OUT> action) {
            if (!isParallel()) {
                sourceStageSpliterator().forEachRemaining(action);
            }
            else {
                super.forEachOrdered(action);
            }
        }
    }

Object类下的本地方法

  • getClass
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface IntrinsicCandidate {
}

@IntrinsicCandidate
    public final native Class<?> getClass();
  • hashCode
    @IntrinsicCandidate
    public native int hashCode();
  • clone
    @IntrinsicCandidate
    protected native Object clone() throws CloneNotSupportedException;
  • notify
    @IntrinsicCandidate
    public final native void notify();
  • notifyAll
    @IntrinsicCandidate
    public final native void notifyAll();
  • wait
    public final native void wait(long timeoutMillis) throws InterruptedException;

stream()方法

参考

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
    public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head<>(spliterator,
                                            StreamOpFlag.fromCharacteristics(spliterator),
                                            parallel);
    }

java.util.function 中的 consumer 以及 Stream接口中的接口Builder

Java中的自我类型引用

interface BaseStream<T, S extends BaseStream<T, S>> 

这种设计在Java的流式APi中被广泛使用,例如Stream IntStream接口。

T 是第一个泛型参数,代表流中的元素类型。例如,在Stream< String> 中,T就是String。

S 是第二个泛型参数,它表示一个具体的子类型,并且必须是BaseStream的某个实现类或者子接口。

S extends BaseStream <T,S> 这种设计允许你在接口中返回当前类型的实例,从而实现链式调用。

也就是说,我这个接口的实现类支持返回自己,从而再次调用stream中的方法。

参考

如果没有子类型引用会是这样:

public interface BaseStream<T> {
    BaseStream<T> filter(Predicate<? super T> predicate);
    BaseStream<T> map(Function<? super T, ? extends T> mapper);
}

那么我方法返回值就无法进行链式调用,因为无法访问stream的特有方法

今日灵感(文字功底有限)

到底什么是学习:无论是Spring轻量级框架,数据库,还是中间件八股文算法。这些东西的本质好像都没有变过,就是解决问题。所以学习就是一个不断解决问题的过程,如果你看到了问题却没有解决,选择安于现状或者取捷径,这无疑会毁了你自己。如果你对陌生问题急切地寻求答案,也许你需要冷静一下。如果你决定去寻找经典问题、母题,亲身体验解决过程后再附上一个小故事,这样,史书上的一页就轻轻印刻在我们后来人的灵魂中。