Java的wait()、notify()学习三部曲之二:修改JVM源码看参数

简介: 线程同步相关的JVM源码分析系列之二,修改源码查看运行时的虚拟机参数

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos

本篇概览

  • 在上一章《 Java的wait()、notify()学习三部曲之一:JVM源码分析》中,我们通过JVM源码分析了线程同步的相关操作,但还是留下了一些疑惑未解:在notify()和释放锁的时候,对等待锁的线程的处理有多个分支,具体走到哪个分支视Policy和QMode的值而定,今天我们实战一次,修改JVM源码将这两个参数在虚拟机运行的时候打印出来看看;
  • 编译JVM源码需要搭建编译环境,推荐使用docker,因为我已准备好了一个完善的编译环境镜像,详情请参照《极速体验编译openjdk8(docker环境》)
  • 如果您用的是linxu或Mac操作系统,那么可直接安装官方docker软件;
  • 如果您用的是win10专业版,也能直接安装官方docker软件;
  • 如果是win10家庭版是无法安装docker的,这时可以装vmware,再安装linux系统,推荐centos7,再在centos7上安装docker,参考《极速体验编译openjdk8(docker环境》)
  • 在docker上启动了bolingcavalry/bolingcavalryopenjdk:0.0.1镜像后:

a. 执行docker exec -it compilejdk/bin/bash进入容器;
b. 执行vi /usr/local/openjdk/hotspot/src/share/vm/runtime/objectMonitor.cpp打开要修改的文件;
c. 找到方法void ObjectMonitor::notify(TRAPS),找到方法内代码:“int Policy = Knob_MoveNotifyee ;”,在下面新增一行“printf("* Policy : %d\n", Policy);”,如下图:

这里写图片描述

d. 找到方法void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS),找到方法内代码"int QMode = Knob_QMode ;",在下面新增一行“printf("* QMode : %d\n", QMode);”,如下图:

这里写图片描述

e. 在/usl/local/openjdk/目录下执行./configure --with-debug-level=slowdebug完成配置检查;
f. 在/usl/local/openjdk/目录下执行bluemake all ZIP_DEBUGINFO_FILES=0 DISABLE_HOTSPOT_OS_VERSION_CHECK=OK CONF=linux-x86_64-normal-server-slowdebug开始编译,大约20分钟,编译完成,如下图:

这里写图片描述

g. 编译完成后进入目录,创建文件NotifyDemo.java文件,内容如下:

public class NotifyDemo {

    private static void sleep(long sleepVal){
        try{
            Thread.sleep(sleepVal);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private static void log(String desc){
        System.out.println(Thread.currentThread().getName() + " : " + desc);
    }

    Object lock = new Object();

    public void startThreadA(){
        new Thread(() -> {
            synchronized (lock){
                log("get lock");
                startThreadB();
                log("start wait");
                try {
                    lock.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }

                log("get lock after wait");
                log("release lock");
            }
        }, "thread-A").start();
    }

    public void startThreadB(){
        new Thread(()->{
            synchronized (lock){
                log("get lock");
                startThreadC();
                sleep(100);
                log("start notify");
                lock.notify();
                log("release lock");

            }
        },"thread-B").start();
    }

    public void startThreadC(){
        new Thread(() -> {
            synchronized (lock){
                log("get lock");
                log("release lock");
            }
        }, "thread-C").start();
    }

    public static void main(String[] args){
        new NotifyDemo().startThreadA();
    }
}

h. 在此目录下执行./javac NotifyDemo.java编译源码;
i. 在此目录下执行./java NotifyDemo执行class,可以看到输出如下图:

这里写图片描述

  • 如上图所示,已将运行时的Policy和QMode打印出来,我们来分析一下吧:
  • 首先,Policy=2,表示线程A从等待队列_WaitSet中被取出,又因为_EntryList为空,所以A放入了_EntryList首位,BlOCKING状态的线程C在_cxq,所以A和C放在不同的队列中:

这里写图片描述

  • 其次,QMode=0,在ObjectMonitor::exit方法中,对QMode等于1、2、3、4的时候都有特殊处理(例如从_EntryList中取出数据),但是对QMode等于0没有特殊处理,而是依次从_EntryList中取出线程来唤醒,如下图,由于A放在_EntryList中,所以A总是先唤醒;

这里写图片描述

  • 通过日志确定参数值,在结合代码分析,我们把上一章的遗留问题已经搞清楚了,在下一章中,我们会继续修改源码,操控线程A和C对锁的抢占顺序。

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关文章
|
监控 Java Unix
6个Java 工具,轻松分析定位 JVM 问题 !
本文介绍了如何使用 JDK 自带工具查看和分析 JVM 的运行情况。通过编写一段测试代码(启动 10 个死循环线程,分配大量内存),结合常用工具如 `jps`、`jinfo`、`jstat`、`jstack`、`jvisualvm` 和 `jcmd` 等,详细展示了 JVM 参数配置、内存使用、线程状态及 GC 情况的监控方法。同时指出了一些常见问题,例如参数设置错误导致的内存异常,并通过实例说明了如何排查和解决。最后附上了官方文档链接,方便进一步学习。
3288 4
|
消息中间件 运维 监控
加一个JVM参数,让系统可用率从95%提高到99.995%
本文针对一个高并发(10W+ QPS)、低延迟(毫秒级返回)的系统因内存索引切换导致的不稳定问题,深入分析并优化了JVM参数配置。通过定位问题根源为GC压力大,尝试了多种优化手段:调整MaxTenuringThreshold、InitialTenuringThreshold、AlwaysTenure等参数让索引尽早晋升到老年代;探索PretenureSizeThreshold和G1HeapRegionSize实现索引直接分配到老年代;加速索引复制过程以及升级至JDK11使用ZGC。
851 82
加一个JVM参数,让系统可用率从95%提高到99.995%
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
473 27
|
11月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
729 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
Java程序员必学:JVM架构完全解读
Java 虚拟机(JVM)是 Java 编程的核心,深入理解其架构对开发者意义重大。本文详细解读 JVM 架构,涵盖类加载器子系统、运行时数据区等核心组件,剖析类加载机制,包括加载阶段、双亲委派模型等内容。阐述内存管理原理,介绍垃圾回收算法与常见回收器,并结合案例讲解调优策略。还分享 JVM 性能瓶颈识别与调优方法,分析 Java 语言特性对性能的影响,给出数据结构选择、I/O 操作及并发同步处理的优化技巧,同时探讨 JVM 安全模型与错误处理机制,助力开发者提升编程能力与程序性能。
Java程序员必学:JVM架构完全解读
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
806 29
JVM简介—1.Java内存区域
|
存储 运维 Kubernetes
Java启动参数JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
本文介绍了Java虚拟机(JVM)常用启动参数配置,包括设置初始堆内存(-Xms512m)、最大堆内存(-Xmx1024m)及内存溢出时生成堆转储文件(-XX:+HeapDumpOnOutOfMemoryError),用于性能调优与故障排查。
1306 0
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
473 28
|
存储 监控 算法
Java JVM 面试题
Java JVM(虚拟机)相关基础面试题
474 4