最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

OpenJDK源碼解析之System.out.println詳解

 更新時(shí)間:2021年04月25日 16:02:58   作者:黃智霖-blog  
這篇文章主要介紹了OpenJDK源碼解析之System.out.println詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下

一、前戲

可能不少小伙伴習(xí)慣在代碼中使用sout打印一些信息,就像這樣:

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

做為一位資深干碼人,本著弘揚(yáng)黨求真務(wù)實(shí)的精神,必須得來看看這個(gè)sout有何玄機(jī)~~

首先看調(diào)用就知道,out是System類的一個(gè)公共靜態(tài)成員變量,進(jìn)入System.java中:

public final static PrintStream out = null;

嗯,不止是public,還是final的。不管,來找找out是在哪里賦值的。。。。。。日嘛找半天沒找到?那就試試直接在類中搜索: out = ,結(jié)果如下:

在這里插入圖片描述

完?duì)僮樱麄€(gè)System類一共將近1300行的代碼,只找到一個(gè)和out賦值相關(guān)的,還是啥子局部變量fdOut,看起來和out屬性沒有什么關(guān)聯(lián)啊~ 往上滑滑看看這個(gè)是什么方法:

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不是給我們調(diào)用的,這個(gè)方法會(huì)在vm線程初始化后被虛擬機(jī)調(diào)用。其定義在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(類元信息在虛擬機(jī)中的結(jié)構(gòu)表示,就是一個(gè)c++中的類),在vmSymbols.hpp中找找java_lang_System對(duì)應(yīng)的值:

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

可以看到代表的就是java.lang.System類,同時(shí),initializeSystemClass_name也定義在vmSymbols.hpp中:

template(initializeSystemClass_name,                "initializeSystemClass")   

這里使用JavaCalls::call_static就是調(diào)用java.lang.System的靜態(tài)方法initializeSystemClass。還要再啰嗦一句,call_initializeSystemClass是在何處調(diào)用的?我們回到thread.cpp中,進(jìn)入Threads::create_vm方法,在其中找到了call_initializeSystemClass方法的調(diào)用:

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

然后看看Threads::create_vm是在何處調(diào)用的,我們進(jìn)入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 {
		......	
	}
	......
}

現(xiàn)在已經(jīng)知道了,JVM啟動(dòng)后會(huì)在初始化JVM的時(shí)候調(diào)用CreateJavaVM,進(jìn)而調(diào)用initializeSystemClass方法。但是out屬性是如何設(shè)置的呢?我們回到j(luò)ava.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);
    }

這個(gè)方法中唯一和out相關(guān)的就是這個(gè)setOut0方法調(diào)用了,我們來看看這個(gè)方法:

private static native void setOut0(PrintStream out);

嗯,這個(gè)是一個(gè)native方法,沒辦法,又只有回到JVM源碼了。怎么找呢?首選組裝一下本地方法名,根據(jù)規(guī)則setOut0對(duì)應(yīng)的本地方法應(yīng)該叫: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靜態(tài)成員,嗯,這個(gè)就是我們java.lang.System類的靜態(tài)成員out:

public final static PrintStream out;

然后將stream參數(shù)賦值給它,這個(gè)stream就是initializeSystemClass方法中通過newPrintStream方法創(chuàng)建的PrintStream 對(duì)象。到現(xiàn)在我們已經(jīng)明白了,out原來是這樣賦值的,真麻煩~

趁熱打鐵,弄明白了out,接下來看看println。既然out是PrintStream對(duì)象,那么到PrintStream中看看println方法:

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

Java代碼看起來是要親切多了,但是這個(gè)synchronized是個(gè)什么鬼???

三、坑?

前面我們發(fā)現(xiàn)println方法竟然有個(gè)synchronized關(guān)鍵字,經(jīng)常在項(xiàng)目中使用sout的小伙伴會(huì)不會(huì)感覺腦袋嗡嗡的?為什么要嗡嗡?不要忘記我們前面通過JVM源碼跟蹤的System.out的賦值過程,這個(gè)out可是單例!你個(gè)加了synchronized(this)的虛方法,還是單例的,如果在系統(tǒng)中進(jìn)行并發(fā)使用,后果不用我多說吧?

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

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

因?yàn)閜rint方法好像沒有加鎖?。?/p>

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();
            }
        }
		......
    }

四、總結(jié)

不知道有沒有小伙伴經(jīng)常在項(xiàng)目中使用System.out.println來輸出"日志"?千萬不要亂用喲,不然說不定哪天就被out了~~~

到此這篇關(guān)于OpenJDK源碼解析之System.out.println詳解的文章就介紹到這了,更多相關(guān)OpenJDK源碼解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot啟動(dòng)時(shí)運(yùn)行特定代碼的多種方式小結(jié)

    SpringBoot啟動(dòng)時(shí)運(yùn)行特定代碼的多種方式小結(jié)

    SpringBoot提供了多種方式在應(yīng)用程序啟動(dòng)時(shí)運(yùn)行特定的代碼,包括CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件機(jī)制和自定義注解等,下面就來具體介紹一下
    2025-01-01
  • Java代理模式實(shí)例分析

    Java代理模式實(shí)例分析

    這篇文章主要介紹了Java代理模式,結(jié)合實(shí)例形式對(duì)比分析了java代理模式的使用方法與相關(guān)操作技巧,需要的朋友可以參考下
    2019-07-07
  • Spring Cloud  Eureka服務(wù)治理的實(shí)現(xiàn)

    Spring Cloud Eureka服務(wù)治理的實(shí)現(xiàn)

    服務(wù)治理是微服務(wù)框架中最為核心和基礎(chǔ)的模塊,它主要是用來實(shí)現(xiàn)各個(gè)微服務(wù)實(shí)例的自動(dòng)化注冊(cè)與發(fā)現(xiàn)。這篇文章主要介紹了Spring Cloud Eureka服務(wù)治理的實(shí)現(xiàn),感興趣的小伙伴們可以參考一下
    2018-06-06
  • Java 客戶端向服務(wù)端上傳mp3文件數(shù)據(jù)的實(shí)例代碼

    Java 客戶端向服務(wù)端上傳mp3文件數(shù)據(jù)的實(shí)例代碼

    這篇文章主要介紹了Java 客戶端向服務(wù)端上傳mp3文件數(shù)據(jù)的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-09-09
  • Java的LinkedHashSet源碼深入講解

    Java的LinkedHashSet源碼深入講解

    這篇文章主要介紹了Java的LinkedHashSet源碼深入講解,LinkedHashSet是HashSet的子類,而由于HashSet實(shí)現(xiàn)了Set接口,因此LinkedHashSet也間接實(shí)現(xiàn)了Set類,LinkedHashSet類屬于java.base模塊,java.util包下,需要的朋友可以參考下
    2023-09-09
  • Java 代理(Proxy)的原理及應(yīng)用

    Java 代理(Proxy)的原理及應(yīng)用

    動(dòng)態(tài)代理技術(shù)就是用來產(chǎn)生一個(gè)對(duì)象的代理對(duì)象的。 我們?cè)陂_發(fā)中之所以要產(chǎn)生一個(gè)對(duì)象的代理對(duì)象,主要用于攔截對(duì)真實(shí)業(yè)務(wù)對(duì)象的訪問。本文主要介紹了Java 代理的使用,感興趣的可以了解一下
    2021-05-05
  • maven無法依賴spring-cloud-stater-zipkin的解決方案

    maven無法依賴spring-cloud-stater-zipkin的解決方案

    這篇文章主要介紹了maven無法依賴spring-cloud-stater-zipkin如何解決,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • Java 正則表達(dá)式詳細(xì)介紹

    Java 正則表達(dá)式詳細(xì)介紹

    本文主要介紹 Java 正則表達(dá)式的內(nèi)容,這里整理了Java 正則表達(dá)式的相關(guān)資料,并詳細(xì)介紹,附有代碼示例,有興趣的小伙伴可以參考下
    2016-09-09
  • rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題

    rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題

    這篇文章主要介紹了rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • SpringBoot整合Swagger Api自動(dòng)生成文檔的實(shí)現(xiàn)

    SpringBoot整合Swagger Api自動(dòng)生成文檔的實(shí)現(xiàn)

    本文主要介紹了SpringBoot整合Swagger Api自動(dòng)生成文檔的實(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評(píng)論

溧水县| 肇庆市| 五莲县| 克拉玛依市| 宣威市| 淮南市| 石景山区| 司法| 福贡县| 松潘县| 英吉沙县| 绥化市| 丰城市| 略阳县| 新邵县| 重庆市| 霍林郭勒市| 青田县| 东乡县| 广丰县| 关岭| 静宁县| 会泽县| 常宁市| 昭觉县| 临安市| 西充县| 松桃| 永吉县| 疏勒县| 会东县| 五原县| 渝中区| 耒阳市| 盐池县| 芷江| 开江县| 砀山县| 安新县| 政和县| 渑池县|