广告
返回顶部
首页 > 资讯 > 后端开发 > Python >OpenJDK源码解析之System.out.println详解
  • 861
分享到

OpenJDK源码解析之System.out.println详解

2024-04-02 19:04:59 861人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、前戏二、JVM源码分析三、坑?四、总结一、前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样: System.out.println("hello wor

一、前戏

可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样:


System.out.println("hello world!")

做为一位资深干码人,本着弘扬党求真务实的精神,必须得来看看这个sout有何玄机~~

首先看调用就知道,out是System类的一个公共静态成员变量,进入System.java中:


public final static PrintStream out = null;

嗯,不止是public,还是final的。不管,来找找out是在哪里赋值的。。。。。。日嘛找半天没找到?那就试试直接在类中搜索: out = ,结果如下:

在这里插入图片描述

完犊子,整个System类一共将近1300行的代码,只找到一个和out赋值相关的,还是啥子局部变量fdOut,看起来和out属性没有什么关联啊~ 往上滑滑看看这个是什么方法:


private static void initializeSystemClass() {
	......
	FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
	FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
	FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
	setIn0(new BufferedInputStream(fdIn));
	setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
	setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));
	......
}

原来如此,看到initializeSystemClass方法,似乎一切都明了了~~

二、JVM源码分析

initializeSystemClass不是给我们调用的,这个方法会在vm线程初始化后被虚拟机调用。其定义在thread.cpp中:


static void call_initializeSystemClass(TRAPS) {
  Klass* k =  SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(), true, CHECK);
  instanceKlasshandle klass (THREAD, k);

  JavaValue result(T_VOID);
  JavaCalls::call_static(&result, klass, vmSymbols::initializeSystemClass_name(),
                                         vmSymbols::void_method_signature(), CHECK);
}

首先获取Klass(类元信息在虚拟机中的结构表示,就是一个c++中的类),在vmSymbols.hpp中找找java_lang_System对应的值:


 template(java_lang_System,                          "java/lang/System") 

可以看到代表的就是java.lang.System类,同时,initializeSystemClass_name也定义在vmSymbols.hpp中:


template(initializeSystemClass_name,                "initializeSystemClass")   

这里使用JavaCalls::call_static就是调用java.lang.System的静态方法initializeSystemClass。还要再啰嗦一句,call_initializeSystemClass是在何处调用的?我们回到thread.cpp中,进入Threads::create_vm方法,在其中找到了call_initializeSystemClass方法的调用:


jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
	......
	 call_initializeSystemClass(CHECK_0);
	......
}

然后看看Threads::create_vm是在何处调用的,我们进入jni.cpp,找到JNI_CreateJavaVM方法:


_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
	......
	result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
	if (result == JNI_OK) {
		......
	} else {
		......	
	}
	......
}

现在已经知道了,JVM启动后会在初始化JVM的时候调用CreateJavaVM,进而调用initializeSystemClass方法。但是out属性是如何设置的呢?我们回到java.lang.System#initializeSystemClass方法:


private static void initializeSystemClass() {
	......
	FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
	setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
	......
}

private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
       if (enc != null) {
            try {
                return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
            } catch (UnsupportedEncodingException uee) {}
        }
        return new PrintStream(new BufferedOutputStream(fos, 128), true);
    }

这个方法中唯一和out相关的就是这个setOut0方法调用了,我们来看看这个方法:


private static native void setOut0(PrintStream out);

嗯,这个是一个native方法,没办法,又只有回到JVM源码了。怎么找呢?首选组装一下本地方法名,根据规则setOut0对应的本地方法应该叫:Java_java_lang_System_setOut0,我们到源码中找找,然后在System.c中找到了该方法


JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

首先获取了java.io.PrintStread类型的out静态成员,嗯,这个就是我们java.lang.System类的静态成员out:


public final static PrintStream out;

然后将stream参数赋值给它,这个stream就是initializeSystemClass方法中通过newPrintStream方法创建的PrintStream 对象。到现在我们已经明白了,out原来是这样赋值的,真麻烦~

趁热打铁,弄明白了out,接下来看看println。既然out是PrintStream对象,那么到PrintStream中看看println方法:


public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

Java代码看起来是要亲切多了,但是这个synchronized是个什么鬼???

三、坑?

前面我们发现println方法竟然有个synchronized关键字,经常在项目中使用sout的小伙伴会不会感觉脑袋嗡嗡的?为什么要嗡嗡?不要忘记我们前面通过JVM源码跟踪的System.out的赋值过程,这个out可是单例!你个加了synchronized(this)的虚方法,还是单例的,如果在系统中进行并发使用,后果不用我多说吧?

那可能有人就要说了,那我不用println(“hello world!”)了嘛,我换成print总行了吧?


System.out.print("hello world!");
System.out.print("\n");

因为print方法好像没有加啊:


public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }

那我们看看write方法,不好意思:


private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
		......
    }

四、总结

不知道有没有小伙伴经常在项目中使用System.out.println来输出"日志"?千万不要乱用哟,不然说不定哪天就被out了~~~

到此这篇关于Openjdk源码解析之System.out.println详解的文章就介绍到这了,更多相关OpenJDK源码解析内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: OpenJDK源码解析之System.out.println详解

本文链接: https://www.lsjlt.com/news/124562.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • OpenJDK源码解析之System.out.println详解
    目录一、前戏二、JVM源码分析三、坑?四、总结一、前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样: System.out.println("hello wor...
    99+
    2022-11-12
  • Java源码解析之详解ImmutableMap
    一、案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样—— public static final Map<St...
    99+
    2022-11-12
  • Java源码解析之详解ReentrantLock
    ReentrantLock ReentrantLock是一种可重入的互斥锁,它的行为和作用与关键字synchronized有些类似,在并发场景下可以让多个线程按照一定的顺序访问同一资...
    99+
    2022-11-12
  • python源码剖析之PyObject详解
    目录一、Python中的对象1.1 对象机制的基石PyObject二、类型对象2.1 对象的创建2.2 对象的行为2.3 类型的类型三、Python的多态性四、引用计数五、Pytho...
    99+
    2022-11-12
  • Android源码解析之属性动画详解
    前言 大家在日常开发中离不开动画,属性动画更为强大,我们不仅要知道如何使用,更要知道他的原理。这样,才能得心应手。那么,今天,就从最简单的来说,了解下属性动画的原理。 Obj...
    99+
    2022-06-06
    属性 动画 Android
  • Go并发之RWMutex的源码解析详解
    目录使用场景源码解析RWMutex结构体Lock()方法Unlock()方法RLock()方法RUnlock()方法RWMutex是一个支持并行读串行写的读写锁。RWMutex具有写...
    99+
    2023-03-15
    Go RWMutex源码 Go RWMutex
  • MyBatis3源码解析之如何获取数据源详解
    目录前言jdbc传统JDBC弊端思考源码分析获取数据源总结前言 上文讲的MyBatis部署运行且根据官网运行了一个demo:一步到位部署运行MyBatis3源码<保姆级>...
    99+
    2022-11-13
  • vue2源码解析之全局API实例详解
    目录前言Vue.extend()基本使用整体源码Vue.nextTick,Vue.set,Vue.deleteVue.directive、Vue.filter、Vue.co...
    99+
    2022-11-13
    vue2全局api vue2.0源码解析 vuejs源码解析
  • Vue源码分析之虚拟DOM详解
    为什么需要虚拟dom? 虚拟DOM就是为了解决浏览器性能问题而被设计出来的。例如,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内...
    99+
    2022-11-12
  • java TreeMap源码解析详解
    java TreeMap源码解析详解 在介绍TreeMap之前,我们来了解一种数据结构:排序二叉树。相信学过数据结构的同学知道,这种结构的数据存储形式在查找的时候效率非常高。如图所示,这种数据结构是以二叉树为基础的,所有的左孩子的...
    99+
    2023-05-31
    java treemap 源码
  • Java源码解析之HashMap的put、resize方法详解
    目录一、HashMap 简介二、源码分析2.1 继承和实现2.2 属性2.3 节点类型Node内部类2.4 红黑树的节点三、构造方法3.1 构造器13.2 构造器23.3 构造器33...
    99+
    2022-11-12
  • Python源码解析之List
    目录一、列表结构体二、创建列表三、添加元素四、移除元素五、清空六、销毁一、列表结构体 创建列表C语言底层的结构体 lists = [] list.append('name') list.append('age')...
    99+
    2022-06-02
    Python List python创建列表
  • 解析Linux源码之epoll
    目录一、前言二、简单的epoll例子2.1、epoll_create2.2、struct eventpoll2.3、epoll_ctl(add)2.4、ep_insert2.5、tfile->f_op->...
    99+
    2022-06-03
    Linux 源码 Linux epoll
  • Java源码解析之ClassLoader
    目录一、前言二、java 中的 ClassLoader三、Android 中的 ClassLoader四、双亲委派机制五、源码分析一、前言 一个完整的Java应用程序,当程序在运行时...
    99+
    2022-11-12
  • Spring源码解析之Configuration
    目录一、@Configuration1.1 未加@Configuration1.2 加上@Configuration1.3 Cglib动态代理二、源码跟踪2.1 Annotation...
    99+
    2022-11-12
  • Java源码解析之LinkedHashMap
    目录一、成员变量二、构造函数三、重要方法一、成员变量 先来看看存储元素的结构吧: static class Entry<K,V> extends HashMap.No...
    99+
    2022-11-12
  • Java源码解析之ConcurrentHashMap
    早期 ConcurrentHashMap,其实现是基于: 分离锁,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈...
    99+
    2022-11-12
  • java集合类源码分析之Set详解
    Set集合与List一样,都是继承自Collection接口,常用的实现类有HashSet和TreeSet。值得注意的是,HashSet是通过HashMap来实现的而TreeSet是通过TreeMap来实现的,所以HashSet和TreeS...
    99+
    2023-05-31
    java 集合类源码 set
  • Reactcommit源码分析详解
    目录总览commitBeforeMutationEffectscommitMutationEffects插入 dom 节点获取父节点及插入位置判断当前节点是否为单节点在对应位置插入节...
    99+
    2022-11-13
    React commit React commit源码
  • SDWebImage源码解析之SDWebImageManager的注解
    ...
    99+
    2023-06-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作