欢迎来到皮皮网网首页

【模拟商城源码】【yersinia源码】【人事 源码】jdk hotspot源码

来源:Tcp定时器源码 时间:2024-11-23 15:43:34

1.Java源码分析 | CharSequence
2.从HotSpot源码,源码深度解读 park 和 unpark
3.MarkWord和Synchronized的源码锁升级机制详解(JDK8)
4.JDK基础概念及目录结构
5.java是如何调用native方法?hotspot源码分析必会技能
6.【tcp】关于tcp socket出现的“connection reset by peer”和“broken pipe”

jdk hotspot源码

Java源码分析 | CharSequence

       本文基于 OracleJDK ,HotSpot 虚拟机,源码深入探讨了 CharSequence 接口在 Java 中的源码角色与应用。

       CharSequence 定义

       CharSequence 是源码 java.lang 包下的一个接口,专门用于描述字符序列,源码模拟商城源码即字符串。源码它提供对多种不同类型的源码 char 序列的统一只读访问,包括 String、源码StringBuffer、源码StringBuilder 和 CharBuffer 等。源码

       Unicode 规范与 char 值表示

       扩展 char 数据类型基于原始 Unicode 规范。源码Unicode 标准定义了合法代码点的源码范围是从 U+ 到 U+FFFF。这些代码点分为基本多语言平面(BMP)和补充平面。源码一个 char 值表示 BMP 代码点,源码可为代理代码点或 UTF- 编码的代码单元。一个 int 值表示所有 Unicode 代码点,包括补充代码点,其中低(最低有效) 位用于表示代码点,高(最高) 位必须为零。

       常用方法解析

       CharSequence 接口提供了多个核心方法,包括:

       length() 方法返回字符序列的长度,即 位 char 的个数。

       charAt(int index) 方法返回指定索引处的 char 值,索引范围从零到 length() - 1。

       subSequence(int start, int end) 方法返回指定范围的子序列,长度为 end - start。

       toString() 方法将序列转换为字符串。

       chars() 方法返回序列中的yersinia源码 int 值流,适用于内部循环优化。

       codePoints() 方法返回序列中的代码点值流。

       compare(CharSequence cs1, CharSequence cs2) 方法在 Java 中引入,用于按字典顺序比较两个 CharSequence 实例。

       这些方法为开发者提供了高效处理字符序列的工具,确保 Java 应用程序能够灵活应对复杂字符串操作。

从HotSpot源码,深度解读 park 和 unpark

       我最近建立了一个在线自习室(App:番茄ToDO)用于相互监督学习,感兴趣的小伙伴可以加入。自习室加入码:D5A7A

       Java并发包下的类大多基于AQS(AbstractQueuedSynchronizer)框架实现,而AQS线程安全的实现依赖于两个关键类:Unsafe和LockSupport。

       其中,Unsafe主要提供CAS操作(关于CAS,在文章《读懂AtomicInteger源码(多线程专题)》中讲解过),LockSupport主要提供park/unpark操作。实际上,park/unpark操作的最终调用还是基于Unsafe类,因此Unsafe类才是核心。

       Unsafe类的实现是由native关键字说明的,这意味着这个方法是原生函数,是用C/C++语言实现的,并被编译成了DLL,由Java去调用。

       park函数的作用是将当前调用线程阻塞,而unpark函数则是唤醒指定线程。

       park是等待一个许可,unpark是为某线程提供一个许可。如果线程A调用park,人事 源码除非另一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上。每次调用一次park,需要有一个unpark来解锁。

       并且,unpark可以先于park调用,但不管unpark先调用多少次,都只提供一个许可,不可叠加。只需要一次park来消费掉unpark带来的许可,再次调用会阻塞。

       在Linux系统下,park和unpark是通过Posix线程库pthread中的mutex(互斥量)和condition(条件变量)来实现的。

       简单来说,mutex和condition保护了一个叫_counter的信号量。当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。当_counter=0时线程阻塞,当_counter>0时直接设为0并返回。

       每个Java线程都有一个Parker实例,Parker类的部分源码如下:

       由源码可知,Parker类继承于PlatformParker,实际上是用Posix的mutex和condition来实现的。Parker类里的_counter字段,就是用来记录park和unpark是否需要阻塞的标识。

       具体的bytebuf 源码执行逻辑已经用注释标记在代码中,简要来说,就是检查_counter是不是大于0,如果是,则把_counter设置为0,返回。如果等于零,继续执行,阻塞等待。

       unpark直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程。源码如下:

       (如果不会下载JVM源码可以后台回复“jdk”,获得下载压缩包)

MarkWord和Synchronized的锁升级机制详解(JDK8)

       锁升级机制在JDK 后已经废弃,本文所述仅为面试中常问的低版本synchronized的锁升级机制,具体新机制需查阅最新JDK源码。

       在Java并发编程中,synchronized是最常用的关键字,用于保护代码块和方法在多线程场景下的并发安全问题。synchronized锁基于对象实现,通常用于修饰同步方法和同步代码块。

       下面给出一段简单的Java代码,包含三种synchronized的使用方法,通过反编译查看字节码,了解synchronized的实现原理。

       修饰方法时,synchronized关键字会在方法的字节码中添加ACC_SYNCHRONIZED标志,确保只有一个线程可以同时执行该方法。快快源码synchronized修饰静态方法同样添加此标志。

       修饰代码块时,synchronized关键字会在相应的指令区间添加monitorenter和monitorexit指令,JVM通过这两个指令保证多线程状态下的同步。

       ACC_SYNCHRONIZED、monitorenter、monitorexit的解释,来源于官网介绍和chatgpt翻译。

       方法级的synchronized隐式执行,通过ACC_SYNCHRONIZED标志区分,方法调用指令会检查此标志。调用设置ACC_SYNCHRONIZED的方法时,线程进入monitor,执行方法,并在方法调用正常完成或异常中断时退出monitor。

       monitorenter指令尝试获取与对象相关联的monitor的所有权,monitorexit指令执行时,对象相关联的monitor的进入计数减1。

       Monitor是Java中用于实现线程同步和互斥的机制,每个Java对象都与一个Monitor相关联,主要目的是确保在任何给定时间,只有一个线程能够执行与特定对象相关联的临界区代码。

       ObjectMonitor是JDK 的HotSpot源码中定义的Monitor,其核心参数包括EntrySet、WaitSet和一个线程的owner。

       Java对象与monitor关联,需要了解Java对象布局和对象头的相关知识。

       在JDK 1.6之前,synchronized需要依赖于底层操作系统的Mutex Lock实现,导致效率低下。在JDK 1.6之后,引入了偏向锁与轻量锁来减小获取和释放锁的性能消耗。

       锁升级分为四种状态:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,锁会随着线程的竞争情况逐渐升级,但锁升级是不可逆的。

       偏向锁在没有其他线程竞争时,持有偏向锁的线程不会主动释放,偏向锁的释放时机是在其他线程竞争该锁时。

       轻量级锁使用CAS操作,尝试将对象头部的锁记录指针替换为指向线程栈上的锁记录。轻量级锁的撤销意味着不再通过自旋的方式等待获取锁,而是直接阻塞线程。

       重量级锁状态下,对象的头部会指向一个Monitor对象,该Monitor对象负责管理锁的获取和释放。

       JDK 1.6及之后版本引入了自适应自旋锁、锁消除和锁粗化等锁优化策略,以进一步提升synchronized的性能。

       自适应自旋锁根据前一次在相同锁上的自旋时间以及锁的持有者状态来动态决定自旋的上限次数。

       锁消除是JVM在JIT编译期间进行的优化,通过逃逸分析来消除不可能存在共享资源竞争的锁。

       锁粗化是通过将加锁范围扩展到整个操作序列的外部,降低加锁解锁的频率来减少性能损耗。

       本文总结了JDK8中synchronized的锁升级机制,介绍了无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁的升级流程,以提升并发效率。

JDK基础概念及目录结构

       探索Java开发基石:JDK概念与目录结构详解</

       Java开发环境的基石就是Java Development Kit (JDK),它不仅包含了Java编译器、调试工具和javadoc等实用工具,而且是将Java源代码编译为跨平台可执行字节码的关键。默认情况下,JDK使用Hotspot VM进行解释执行,OpenJDK则是其开源版本,尽管在授权协议和源代码完整性方面与SUN/Oracle JDK有所不同,但都为Java编程提供了强大的支持。

       深入探讨JDK的内部结构,我们先来看一个典型的目录结构示例(以CentOS 7和JDK 1.8为例):bin、COPYRIGHT、db、include、jre、lib、LICENSE、man、README和src.zip等文件夹,每一个都承载着特定的功能和职责。

bin</: 这个目录犹如Java开发者的瑞士军刀,存储了Java工具(如java、javac和javadoc)以及关键的工具jar(如dt.jar和tools.jar),是日常开发中不可或缺的部分。

COPYRIGHT</: 保护着JDK的版权信息,提醒我们尊重和遵守版权法规。

db</: 包含Java数据库相关资源,但现代Java应用更多依赖于数据库驱动,而非这个目录。

include</: C语言头文件的天堂,如JNI(Java Native Interface)头文件JNI.h,用于Java与C/C++代码的交互。

lib</: 精华所在,Java类库的宝库,包含dt.jar和tools.jar,它们在CLASSPATH中占据重要位置,如rt.jar(核心类库,如java.lang, java.io, java.net, java.util)和平台特定库。

src.zip</: 提供Java类库源码,包括rt.jar关键部分和启动器源码,是深入理解Java源码的绝佳入口。

jre</: Java运行环境的核心,包含bin(如java[.exe])、lib(rt.jar和动态链接库)以及针对不同架构的子目录,如amd(JVM库libjvm.so)。

dt.jar</:尽管在现代GUI开发中使用较少,但Swing包依然对Java图形界面设计至关重要。

tools.jar</:工具类库,包括编译器和文档生成器,如javac.exe和javadoc.exe,简化了开发者的工作流程。

       CLASSPATH设置:$.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar,这个环境变量确保了IDE能正确引用JDK的类库。

       对于IDE用户,如Mac上的IntelliJ IDEA,类库结构的洞察有助于理解代码执行的底层逻辑。它展示了代码是如何通过JDK的各个组件协同工作的。

       最后,虽然我们已经掌握了JDK的基础,但Java世界的深奥远不止于此。深入阅读源代码,探究Java类库和JVM的工作原理,是提升编程技能和理解力的不二法门。

java是如何调用native方法?hotspot源码分析必会技能

       在深入研究JDK源码,如并发包和Thread相关部分时,往往会遇到native修饰的方法,它们隐藏在层层方法的底层。native方法的存在并非偶然,它是解决Java语言与操作系统直接交互的关键。Java作为高层语言,需要JVM作为桥梁,将Java指令转换为可以直接操作系统的C或C++代码,这就是native方法的用武之地。

       JDK、JRE和JVM的关系是这样的:JDK包含JRE,其中的JVM负责执行Java代码并进行操作系统间的转换。在OpenJDK源码中,特别是hotspot实现的JVM中,能找到native方法的具体实现。JNI(Java Native Interface)技术用于模拟Java调用C或C++编写的native方法,确保跨平台的兼容性。

       让我们通过实践来理解这个过程。首先,创建一个简单的Java类,通过javac编译,生成JavaCallC.class文件。然后使用javah命令生成JavaCallC.h头文件,这是C语言调用Java的关键部分,需要与Java代码中的native方法签名匹配。接着,编写C代码(Cclass.c),编译成动态链接库libJavaCallC.so,并将库文件路径添加到LD_LIBRARY_PATH环境变量中。

       最后,执行JavaCallC命令,如果一切顺利,会看到"Java_JavaCallC_cMethod call succ"的输出,表明Java成功调用了native方法。在尝试过程中可能会遇到各种问题,但通过一步步的调试和学习,我们可以逐步掌握这个过程。

【tcp】关于tcp socket出现的“connection reset by peer”和“broken pipe”

       åœ¨socket通信过程中,经常发现客户端或者服务器的日志中出现“broken pipe”或者“connection reset by peer”的错误提示。

        以前一直以为自己理解了这两个错误异常提示所包含的意义,而实际理解完全错误。

        我的错误理解和下面这段来自blogspot的表述差不多:

        ```

        Maybe I'm just dumb, but I always thought "broken pipe" meant, "the other end of this socket closed before I finished sending something" and "connection reset by peer" meant, well, roughly the same thing. (As well as indicating some slightly more esoteric problems.)

        Turns out though, "broken pipe" actually means "I just tried to send something and the socket was already closed to sending."

        So in the following example, if the other end of (TCP) socket "sock" closes or dies before the write method, "connection reset by peer" will be raised. The next write will give a broken-pipe error, since the socket now knows that further sending is invalid.

        ```

        ```

        try:

            sock.write('foo')

        except:

            pass # connection reset by peer

        sock.write('bar') # broken pipe

        ```

       RST的标志位,这个标识为在如下几种情况下会被设置,以下是我了解的情况,可能还有更多的场景,没有验证:

        1. 当尝试和未开放的服务器端口建立tcp连接时,服务器tcp将会直接向客户端发送reset报文

        2. 双方之前已经正常建立了通信通道,也可能进行过了交互,当某一方在交互的过程中发生了异常,如崩溃等,异常的一方会向对端发送reset报文,通知对方将连接关闭

        3. 当收到TCP报文,但是发现该报文不是已建立的TCP连接列表可处理的,则其直接向对端发送reset报文

        4. ack报文丢失,并且超出一定的重传次数或时间后,会主动向对端发送reset报文释放该TCP连接

       å…¶å®žæˆ‘们java异常里看到的Broken pipe或者Connection reset by peer信息不是jdk或者jvm里定义的,我看到这些关键字往往会首先搜索下jdk或者hotspot源码找到位置进行上下文分析,但是没找到,后面才想到应该是Linux或者glibc里定义的,果然在glibc里看到了如上的描述和定义。

        对于Broken pipe在管道的另外一端没有进程在读的时候就会抛出此异常,Connection reset by peer的描述其实不是很正确,从我的实践来看只描述了一方面,其实在某一端正常close之后,也是可能会有此异常的。

       connection reset by peer”和”broken pipe”出现的场景:

        1)往一个对端已经close的通道写数据的时候,对方的tcp会收到这个报文,并且反馈一个reset报文。当收到reset报文的时候,继续做select读数据的时候就会抛出Connect reset by peer的异常,。

        2)当第一次往一个对端已经close的通道写数据的时候会和上面的情况一样,会收到reset报文。当再次往这个socket写数据的时候,就会抛出Broken pipe了 。根据tcp的约定,当收到reset包的时候,上层必须要做出处理,调用将socket文件描述符进行关闭,其实也意味着pipe会关闭,因此会抛出这个顾名思义的异常。

       ä»Žtcp原理角度理解Broken pipe和Connection Reset by Peer的区别

       /blog////tcp-broken-pipe

       å…³äºŽtcp socket出现的”connection reset by peer“和“broken pipe”

       ////%E5%%B3%E4%BA%8Etcp-socket%E5%%BA%E7%8E%B0%E7%9A%connection-reset-by-peer%E5%%8Cbroken-pipe