<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入OpenJDK源碼--你真的了解System.out.println嗎?

          共 8254字,需瀏覽 17分鐘

           ·

          2021-04-06 09:22

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          76套java從入門到精通實(shí)戰(zhàn)課程分享

          一、前戲

          ??可能不少小伙伴習(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 {
            ...... 
           }
           ......
          }

          ??關(guān)于JNI_CreateJavaVM,在深入openjdk源碼全面理解Java類加載器這篇文章梳理JVM啟動(dòng)過程的時(shí)候提過,這里就不贅述了。

          ?? 現(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中找到了該方法(關(guān)于JNI,可以看看Java深入JVM源碼核心探秘Unsafe(含JNI完整使用流程)):

          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方法好像沒有加鎖啊:

          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了~~~

          ————————————————

          版權(quán)聲明:本文為CSDN博主「黃智霖-blog」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/huangzhilin2015/article/details/115311271






          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 67
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲网站观看视频 | 国产寡妇婬乱A毛片91精品 | 亚洲国产免费视频 | 五月天婷婷色综合 | 2025最新国产毛片 |