標籤: 收購3c

  • Android DecorView 與 Activity 綁定原理分析

    Android DecorView 與 Activity 綁定原理分析

    一年多以前,曾經以為自己對 View 的添加显示邏輯已經有所了解了,事後發現也只是懂了些皮毛而已。經過一年多的實戰,Android 和 Java 基礎都有了提升,是時候該去看看 DecorView 的添加显示。

    概論

    Android 中 Activity 是作為應用程序的載體存在,代表着一個完整的用戶界面,提供了一個窗口來繪製各種視圖,當 Activity 啟動時,我們會通過 setContentView 方法來設置一個內容視圖,這個內容視圖就是用戶看到的界面。那麼 View 和 activity 是如何關聯在一起的呢 ?

     上圖是 View 和 Activity 之間的關係。先解釋圖中一些類的作用以及相關關係:

    • Activity : 對於每一個 activity 都會有擁有一個 PhoneWindow。

    • PhoneWindow :該類繼承於 Window 類,是 Window 類的具體實現,即我們可以通過該類具體去繪製窗口。並且,該類內部包含了一個 DecorView 對象,該 DectorView 對象是所有應用窗口的根 View。
    • DecorView 是一個應用窗口的根容器,它本質上是一個 FrameLayout。DecorView 有唯一一個子 View,它是一個垂直 LinearLayout,包含兩個子元素,一個是 TitleView( ActionBar 的容器),另一個是 ContentView(窗口內容的容器)。

    • ContentView :是一個 FrameLayout(android.R.id.content),我們平常用的 setContentView 就是設置它的子 View 。

    • WindowManager : 是一個接口,裏面常用的方法有:添加View,更新View和刪除View。主要是用來管理 Window 的。WindowManager 具體的實現類是WindowManagerImpl。最終,WindowManagerImpl 會將業務交給 WindowManagerGlobal 來處理。
    • WindowManagerService (WMS) : 負責管理各 app 窗口的創建,更新,刪除, 显示順序。運行在 system_server 進程。

    ViewRootImpl :擁有 DecorView 的實例,通過該實例來控制 DecorView 繪製。ViewRootImpl 的一個內部類 W,實現了 IWindow 接口,IWindow 接口是供 WMS 使用的,WSM 通過調用 IWindow 一些方法,通過 Binder 通信的方式,最後執行到了 W 中對應的方法中。同樣的,ViewRootImpl 通過 IWindowSession 來調用 WMS 的 Session 一些方法。Session 類繼承自 IWindowSession.Stub,每一個應用進程都有一個唯一的 Session 對象與 WMS 通信。

    DecorView 的創建 

    先從 Mainactivity 中的代碼看起,首先是調用了 setContentView;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    該方法是父類 AppCompatActivity 的方法,最終會調用 AppCompatDelegateImpl 的 setContentView 方法:

    // AppCompatDelegateImpl  
    public void setContentView(int resId) { this.ensureSubDecor(); ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290); contentParent.removeAllViews(); LayoutInflater.from(this.mContext).inflate(resId, contentParent); this.mOriginalWindowCallback.onContentChanged(); }

    ensureSubDecor 從字面理解就是創建 subDecorView,這個是根據主題來創建的,下文也會講到。創建完以後,從中獲取 contentParent,再將從 activity 傳入的 id xml 布局添加到裏面。不過大家注意的是,在添加之前先調用 removeAllViews() 方法,確保沒有其他子 View 的干擾。

        private void ensureSubDecor() {
            if (!this.mSubDecorInstalled) {
                this.mSubDecor = this.createSubDecor(); 
                ......
            }
            ......
        }        

     最終會調用 createSubDecor() ,來看看裏面的具體代碼邏輯:

     private ViewGroup createSubDecor() {
            // 1、獲取主題參數,進行一些設置,包括標題,actionbar 等 
            TypedArray a = this.mContext.obtainStyledAttributes(styleable.AppCompatTheme);
            if (!a.hasValue(styleable.AppCompatTheme_windowActionBar)) {
                a.recycle();
                throw new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity.");
            } else {
                if (a.getBoolean(styleable.AppCompatTheme_windowNoTitle, false)) {
                    this.requestWindowFeature(1);
                } else if (a.getBoolean(styleable.AppCompatTheme_windowActionBar, false)) {
                    this.requestWindowFeature(108);
                }
    
                if (a.getBoolean(styleable.AppCompatTheme_windowActionBarOverlay, false)) {
                    this.requestWindowFeature(109);
                }
    
                if (a.getBoolean(styleable.AppCompatTheme_windowActionModeOverlay, false)) {
                    this.requestWindowFeature(10);
                }
    
                this.mIsFloating = a.getBoolean(styleable.AppCompatTheme_android_windowIsFloating, false);
                a.recycle();
                // 2、確保優先初始化 DecorView
                this.mWindow.getDecorView();
                LayoutInflater inflater = LayoutInflater.from(this.mContext);
                ViewGroup subDecor = null;
                // 3、根據不同的設置來對 subDecor 進行初始化
                if (!this.mWindowNoTitle) {
                    if (this.mIsFloating) {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);
                        this.mHasActionBar = this.mOverlayActionBar = false;
                    } else if (this.mHasActionBar) {
                        TypedValue outValue = new TypedValue();
                        this.mContext.getTheme().resolveAttribute(attr.actionBarTheme, outValue, true);
                        Object themedContext;
                        if (outValue.resourceId != 0) {
                            themedContext = new ContextThemeWrapper(this.mContext, outValue.resourceId);
                        } else {
                            themedContext = this.mContext;
                        }
    
                        subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null);
                        this.mDecorContentParent = (DecorContentParent)subDecor.findViewById(id.decor_content_parent);
                        this.mDecorContentParent.setWindowCallback(this.getWindowCallback());
                        if (this.mOverlayActionBar) {
                            this.mDecorContentParent.initFeature(109);
                        }
    
                        if (this.mFeatureProgress) {
                            this.mDecorContentParent.initFeature(2);
                        }
    
                        if (this.mFeatureIndeterminateProgress) {
                            this.mDecorContentParent.initFeature(5);
                        }
                    }
                } else {
                    if (this.mOverlayActionMode) {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);
                    } else {
                        subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);
                    }
    
                    if (VERSION.SDK_INT >= 21) {
                        ViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {
                            public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                                int top = insets.getSystemWindowInsetTop();
                                int newTop = AppCompatDelegateImpl.this.updateStatusGuard(top);
                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), newTop, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
                                }
    
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
                    } else {
                        ((FitWindowsViewGroup)subDecor).setOnFitSystemWindowsListener(new OnFitSystemWindowsListener() {
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = AppCompatDelegateImpl.this.updateStatusGuard(insets.top);
                            }
                        });
                    }
                }
    
                if (subDecor == null) {
                    throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }");
                } else {
                    if (this.mDecorContentParent == null) {
                        this.mTitleView = (TextView)subDecor.findViewById(id.title);
                    }
    
                    ViewUtils.makeOptionalFitsSystemWindows(subDecor);
                    ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);
                    ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);
                    if (windowContentView != null) {
                        while(windowContentView.getChildCount() > 0) {
                            View child = windowContentView.getChildAt(0);
                            windowContentView.removeViewAt(0);
                            contentView.addView(child);
                        }
    
                        windowContentView.setId(-1);
                        contentView.setId(16908290);
                        if (windowContentView instanceof FrameLayout) {
                            ((FrameLayout)windowContentView).setForeground((Drawable)null);
                        }
                    }
                    // 將 subDecor 添加到 DecorView 中
                    this.mWindow.setContentView(subDecor);
                    contentView.setAttachListener(new OnAttachListener() {
                        public void onAttachedFromWindow() {
                        }
    
                        public void onDetachedFromWindow() {
                            AppCompatDelegateImpl.this.dismissPopups();
                        }
                    });
                    return subDecor;
                }
            }
        }
                        

    上面的代碼總結來說就是在做一件事,就是創建 subDecor。攤開來說具體如下:

    1、根據用戶選擇的主題來設置一些显示特性,包括標題,actionbar 等。

    2、根據不同特性來初始化 subDecor;對 subDecor 內部的子 View 進行初始化。

    3、最後添加到 DecorView中。

    添加的具體代碼如下:此處是通過調用 

     // AppCompatDelegateImpl   this.mWindow.getDecorView();
    
     // phoneWindow    public final View getDecorView() {
            if (mDecor == null || mForceDecorInstall) {
                installDecor();
            }
            return mDecor;
        }
     
    
    private void installDecor() {
            mForceDecorInstall = false;
            if (mDecor == null) {
     // 生成 DecorView             mDecor = generateDecor(-1);
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            } else {
     // 這樣 DecorView 就持有了window             mDecor.setWindow(this);
            }
          ......
    }
    
    
       protected DecorView generateDecor(int featureId) {
            // System process doesn't have application context and in that case we need to directly use // the context we have. Otherwise we want the application context, so we don't cling to the // activity.
            Context context;
            if (mUseDecorContext) {
                Context applicationContext = getContext().getApplicationContext();
                if (applicationContext == null) {
                    context = getContext();
                } else {
                    context = new DecorContext(applicationContext, getContext());
                    if (mTheme != -1) {
                        context.setTheme(mTheme);
                    }
                }
            } else {
                context = getContext();
            }
            return new DecorView(context, featureId, this, getAttributes());
       }

    到此,DecorView 的創建就講完了。可是我們似乎並沒有看到 DecorView 是被添加的,什麼時候對用戶可見的。

     WindowManager

    View 創建完以後,那 Decorview 是怎麼添加到屏幕中去的呢?當然是 WindowManager 呢,那麼是如何將 View 傳到 WindowManager 中呢。

    看 ActivityThread 中的 handleResumeActivity 方法:

    // ActivityThread
    public
    void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { ...... final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManager.getService().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; ...... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } // Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r, false /* force */); // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { performConfigurationChangedForActivity(r, r.newConfig); if (DEBUG_CONFIGURATION) { Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig); } r.newConfig = null; } if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) { l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; if (r.activity.mVisibleFromClient) { ViewManager wm = a.getWindowManager(); View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } } r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) {           // 這裏也會調用addview r.activity.makeVisible(); } } r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(new Idler()); }

    上面的代碼主要做了以下幾件事:

    1、獲取到 DecorView,設置不可見,然後通過 wm.addView(decor, l) 將 view 添加到 WindowManager;

    2、在某些情況下,比如此時點擊了輸入框調起了鍵盤,就會調用 wm.updateViewLayout(decor, l) 來更新 View 的布局。

    3、這些做完以後,會調用 activity 的  makeVisible ,讓視圖可見。如果此時 DecorView 沒有添加到 WindowManager,那麼會添加。 

    // Activity
    void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }

     接下來,看下 addview 的邏輯。 WindowManager 的實現類是 WindowManagerImpl,而它則是通過 WindowManagerGlobal 代理實現 addView 的,我們看下 addView 的方法:

    // WindowManagerGlobal  
     public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
               // ......
        
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
               // do this last because it fires off messages to start doing things
                try {
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                } 
    }

    在這裏,實例化了 ViewRootImpl 。同時調用 ViewRootImpl 的 setView 方法來持有了 DecorView。此外這裏還保存了 DecorView ,Params,以及 ViewRootImpl 的實例。

    現在我們終於知道為啥 View 是在 OnResume 的時候可見的呢。

     ViewRootImpl

    實際上,View 的繪製是由 ViewRootImpl 來負責的。每個應用程序窗口的 DecorView 都有一個與之關聯的 ViewRootImpl 對象,這種關聯關係是由 WindowManager 來維護的。

    先看 ViewRootImpl 的 setView 方法,該方法很長,我們將一些不重要的點註釋掉:

       /**
         * We have one child
         */
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
                    ......
                   
                    mAdded = true;
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
    
                    requestLayout();
                    ......
                }
            }
        }

    這裏先將 mView 保存了 DecorView 的實例,然後調用 requestLayout() 方法,以完成應用程序用戶界面的初次布局。

     public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }

    因為是 UI 繪製,所以一定要確保是在主線程進行的,checkThread 主要是做一個校驗。接着調用 scheduleTraversals 開始計劃繪製了。

    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }

    這裏主要關注兩點:

    mTraversalBarrier : Handler 的同步屏障。它的作用是可以攔截 Looper 對同步消息的獲取和分發,加入同步屏障之後,Looper 只會獲取和處理異步消息,如果沒有異步消息那麼就會進入阻塞狀態。也就是說,對 View 繪製渲染的處理操作可以優先處理(設置為異步消息)。

    mChoreographer: 編舞者。統一動畫、輸入和繪製時機。也是這章需要重點分析的內容。

    mTraversalRunnable :TraversalRunnable 的實例,是一個Runnable,最終肯定會調用其 run 方法:

    final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }

    doTraversal,如其名,開始繪製了,該方法內部最終會調用 performTraversals 進行繪製。

      void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }

    到此,DecorView 與 activity 之間的綁定關係就講完了,下一章,將會介紹 performTraversals 所做的事情,也就是 View 繪製流程。 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Java虛擬機詳解(十)——類加載過程

    Java虛擬機詳解(十)——類加載過程

      在上一篇文章中,我們詳細的介紹了Java,那麼這些Class文件是如何被加載到內存,由虛擬機來直接使用的呢?這就是本篇博客將要介紹的——類加載過程。

    1、類的生命周期

      類從被加載到虛擬機內存開始,到卸載出內存為止,其聲明周期流程如下:

      

      上圖中紅色的5個部分(加載、驗證、準備、初始化、卸載)順序是確定的,也就是說,類的加載過程必須按照這種順序按部就班的開始。這裏的“開始”不是按部就班的“進行”或者“完成”,因為這些階段通常是互相交叉混合的進行的,通常會在一個階段執行過程中調用另一個階段。

    2、加載

      “加載”階段是“類加載”生命周期的第一個階段。在加載階段,虛擬機要完成下面三件事:

      ①、通過一個類的全限定名來獲取定義此類的二進制字節流。

      ②、將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。

      ③、在Java堆中生成一個代表這個類的java.lang.Class對象,作為方法區這些數據的訪問入口。

      PS:類的全限定名可以理解為這個類存放的絕對路徑。方法區是JDK1.7以前定義的運行時數據區,而在JDK1.8以後改為元數據區(Metaspace),主要用於存放被Java虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。詳情可以參考這邊該系列的第二篇文章——。

      另外,我們看第一點——通過類的權限定名來獲取定義此類的二進制流,這裏並沒有明確指明要從哪裡獲取以及怎樣獲取,也就是說並沒有明確規定一定要我們從一個 Class 文件中獲取。基於此,在Java的發展過程中,充滿創造力的開發人員在這個舞台上玩出了各種花樣:

      1、從 ZIP 包中讀取。這稱為後面的 JAR、EAR、WAR 格式的基礎。

      2、從網絡中獲取。比較典型的應用就是 Applet。

      3、運行時計算生成。這就是動態代理技術。

      4、由其它文件生成。比如 JSP 應用。

      5、從數據庫中讀取。

      加載階段完成后,虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區中,然後在Java堆中實例化一個 java.lang.Class 類的對象,這個對象將作為程序訪問方法區中這些類型數據的外部接口。

      注意,加載階段與連接階段的部分內容(如一部分字節碼文件的格式校驗)是交叉進行的,加載階段尚未完成,連接階段可能已經開始了。

    3、驗證

      驗證是連接階段的第一步,作用是為了確保 Class 文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。

      我們說Java語言本身是相對安全,因為編譯器的存在,純粹的Java代碼要訪問數組邊界外的數據、跳轉到不存在的代碼行之類的,是要被編譯器拒絕的。但是前面我們也說過,Class 文件不一定非要從Java源碼編譯過來,可以使用任何途徑,包括你很牛逼,直接用十六進制編輯器來編寫 Class 文件。

      所以,如果虛擬機不檢查輸入的字節流,將會載入有害的字節流而導致系統崩潰。但是虛擬機規範對於檢查哪些方面,何時檢查,怎麼檢查都沒有明確的規定,不同的虛擬機實現方式可能都會有所不同,但是大致都會完成下面四個方面的檢查。

    ①、文件格式驗證

      校驗字節流是否符合Class文件格式的規範,並且能夠被當前版本的虛擬機處理。

      一、是否以魔數 0xCAFEBABE 開頭。

      二、主、次版本號是否是當前虛擬機處理範圍之內。

      三、常量池的常量中是否有不被支持的常量類型(檢查常量tag標誌)

      四、指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。

      五、CONSTANT_Utf8_info 型的常量中是否有不符合 UTF8 編碼的數據。

      六、Class 文件中各個部分及文件本身是否有被刪除的或附加的其他信息。

      以上是一部分校驗內容,當然遠不止這些。經過這些校驗后,字節流才會進入內存的方法區中存儲,接下來後面的三個階段校驗都是基於方法區的存儲結構進行的。

    ②、元數據驗證

      第二個階段主要是對字節碼描述的信息進行語義分析,以保證其描述的信息符合Java語言規範要求。

      一、這個類是否有父類(除了java.lang.Object 類之外,所有的類都應當有父類)。

      二、這個類的父類是否繼承了不允許被繼承的類(被final修飾的類)。

      三、如果這個類不是抽象類,是否實現了其父類或接口之中要求實現的所有普通方法。

      四、類中的字段、方法是否與父類產生了矛盾(例如覆蓋了父類的final字段、或者出現不符合規則的重載)

    ③、字節碼驗證

      第三個階段字節碼驗證是整個驗證階段中最複雜的,主要是進行數據流和控制流分析。該階段將對類的方法進行分析,保證被校驗的方法在運行時不會做出危害虛擬機安全的行為。

      一、保證任意時刻操作數棧中的數據類型與指令代碼序列都能配合工作。例如不會出現在操作數棧中放置了一個 int 類型的數據,使用時卻按照 long 類型來加載到本地變量表中。

      二、保證跳轉指令不會跳轉到方法體以外的字節碼指令中。

      三、保證方法體中的類型轉換是有效的。比如把一個子類對象賦值給父類數據類型,這是安全的。但是把父類對象賦值給子類數據類型,甚至賦值給完全不相干的類型,這就是不合法的。

    ④、符號引用驗證

      符號引用驗證主要是對類自身以外(常量池中的各種符號引用)的信息進行匹配性的校驗,通常需要校驗如下內容:

      一、符號引用中通過字符串描述的全限定名是否能夠找到相應的類。

      二、在指定類中是否存在符合方法的字段描述符及簡單名稱所描述的方法和字段。

      三、符號引用中的類、字段和方法的訪問性(private、protected、public、default)是否可以被當前類訪問。

    4、準備

      準備階段是正式為類變量分配內存並設置類變量初始值的階段,這些內存是在方法區中進行分配。

      注意:

      一、上面說的是類變量,也就是被 static 修飾的變量,不包括實例變量。實例變量會在對象實例化時隨着對象一起分配在堆中。

      二、初始值,指的是一些數據類型的默認值。基本的數據類型初始值如下(引用類型的初始值為null):

      

     

       比如,定義 public static int value = 123 。那麼在準備階段過後,value 的值是 0 而不是 123,把 value 賦值為123 是在程序被編譯后,存放在類的構造器方法之中,是在初始化階段才會被執行。但是有一種特殊情況,通過final 修飾的屬性,比如 定義 public final static int value = 123,那麼在準備階段過後,value 就被賦值為123了。

    5、解析

      解析階段是虛擬機將常量池中的符號引用替換為直接引用的過程。

      符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義的定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標不一定已經加載到內存中。

      直接引用(Direct References):直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現內存布局相關的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同。如果有了直接引用,那麼引用的目標必定已經在內存中存在。

      解析動作主要針對類或接口、字段、類方法、接口方法四類符號引用,分別對應於常量池的 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANTS_InterfaceMethodref_info四種類型常量。

    6、初始化

       初始化階段是類加載階段的最後一步,前面過程中,除第一個加載階段可以通過用戶自定義類加載器參与之外,其餘過程都是完全由虛擬機主導和控制。而到了初始化階段,則開始真正執行類中定義的Java程序代碼(或者說是字節碼)。

      在前面介紹的準備階段中,類變量已經被賦值過初始值了,而初始化階段,則根據程序員的編碼去初始化變量和資源。

      換句話來說,初始化階段是執行類構造器<clinit>() 方法的過程

      ①、<clinit>() 方法 是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static{})中的語句合併產生的,編譯器收集的順序是由語句在源文件中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之後的變量,在前面的靜態語句塊中可以賦值,但是不能訪問。

      比如如下代碼會報錯:

      

     

       但是你把第 14 行代碼放到 static 靜態代碼塊的上面就不會報錯了。或者不改變代碼順序,將第 11 行代碼移除,也不會報錯。

      ②、<clinit>() 方法與類的構造函數(或者說是實例構造器<init>()方法)不同,它不需要显示的調用父類構造器,虛擬機會保證在子類的<init>()方法執行之前,父類的<init>()方法已經執行完畢。因此虛擬機中第一個被執行的<init>()方法的類肯定是 java.lang.Object。

      ③、由於父類的<clinit>() 方法先執行,所以父類中定義的靜態語句塊要優先於子類的變量賦值操作。

      ④、<clinit>() 方法對於接口來說並不是必須的,如果一個類中沒有靜態語句塊,也沒有對變量的賦值操作,那麼編譯器可以不為這個類生成<clinit>() 方法。

      ⑤、接口中不能使用靜態語句塊,但仍然有變量初始化的賦值操作,因此接口與類一樣都會生成<clinit>() 方法。但接口與類不同的是,執行接口中的<clinit>() 方法不需要先執行父接口的<clinit>() 方法。只有當父接口中定義的變量被使用時,父接口才會被初始化。

      ⑥、接口的實現類在初始化時也一樣不會執行接口的<clinit>() 方法。

      ⑦、虛擬機會保證一個類的<clinit>() 方法在多線程環境中被正確的加鎖和同步。如果多個線程同時去初始化一個類,那麼只會有一個線程去執行這個類的<clinit>() 方法,其他的線程都需要阻塞等待,直到活動線程執行<clinit>() 方法完畢。如果在一個類的<clinit>() 方法中有很耗時的操作,那麼可能造成多個進程的阻塞。

      比如對於如下代碼:

    package com.yb.carton.controller;
    
    /**
     * Create by YSOcean
     */
    public class ClassLoadInitTest {
    
    
        static class Hello{
            static {
                if(true){
                    System.out.println(Thread.currentThread().getName() + "init");
                    while(true){}
                }
            }
        }
    
        public static void main(String[] args) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"start");
                Hello h1 = new Hello();
                System.out.println(Thread.currentThread().getName()+"run over");
            }).start();
    
    
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"start");
                Hello h2 = new Hello();
                System.out.println(Thread.currentThread().getName()+"run over");
            }).start();
        }
    
    }

    View Code

      運行結果如下:

      

     

       線程1搶到了執行<clinit>() 方法,但是該方法是一個死循環,線程2將一直阻塞等待。

      知道了類的初始化過程,那麼類的初始化何時被觸發呢?JVM大概規定了如下幾種情況:

      ①、當虛擬機啟動時,初始化用戶指定的類。

      ②、當遇到用以新建目標類實例的 new 指令時,初始化 new 指定的目標類。

      ③、當遇到調用靜態方法的指令時,初始化該靜態方法所在的類。

      ④、當遇到訪問靜態字段的指令時,初始化該靜態字段所在的類。

      ⑤、子類的初始化會觸發父類的初始化。

      ⑥、如果一個接口定義了 default 方法,那麼直接實現或間接實現該接口的類的初始化,會觸發該接口的初始化。

      ⑦、使用反射 API 對某個類進行反射調用時,會初始化這個類。

      ⑧、當初次調用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的類。

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • 豐田衝刺燃料電池車,傳產能將擴增至 10 倍以上

    豐田衝刺燃料電池車,傳產能將擴增至 10 倍以上

    日刊工業新聞 3 日,豐田汽車(Toyota)計劃於 2020 年將燃料電池車(FCV)月產能提高至 3,000 台,將達現行的 10 倍以上水準。豐田計劃在 2020 年下半推出 FCV 車「MIRAI」的次代車款。

    據報導,豐田目前利用元町工廠的專用產線生產「MIRAI」,年產能約 3,000 台,依此換算月產能相當於 250 台左右。

    2018 年 MIRAI 全球銷售量約 2,400 台,而豐田目標在 2020 年以後將 FCV 年銷售量提高至 3 萬台以上水準。

    截至台灣時間 3 日上午 10 點 21 分為止,豐田下跌 0.84%。

    豐田於 2018 年 5 月 24 日宣布,為了因應計劃在 2020 年以後將 FCV 全球年銷售量提高至 3 萬台以上水準的目標,決議將增產 FCV 關鍵零件,計劃在愛知縣豐田市的本社工廠廠區內興建新廠房、增產燃料電池堆(Fuel Cell stack),且也計劃在愛知縣三好市的下山工廠內增設用來儲存氫燃料的高壓氫氣槽專用產線。上述新廠預計於 2020 年左右啟用。

    (本文內容由 授權使用。首圖來源: CC BY 2.0)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 搶攻 EV 需求,三菱化學傳倍增美國鋰電池關鍵材料產能

    搶攻 EV 需求,三菱化學傳倍增美國鋰電池關鍵材料產能

    日刊工業新聞 3 日報導,為了搶攻電動車(EV)需求,三菱化學(Mitsubishi Chemical)將擴增鋰離子電池關鍵材料「電解液」產能,計畫於 2019 年度內(2020 年 3 月底前)將美國工廠(位於田納西州曼非斯)電解液年產能提高至 2 萬噸、將達現行的 2 倍水準。

    三菱化學計畫於 2020 年度結束前(2021 年 3 月底前)將電解液全球年產能提高至 8.5 萬噸、將較 2017 年度大增 95%,且計畫將另一項鋰電池關鍵材料「負極材」全球年產能提高至 2.9 萬噸(將較 2017 年度增加 61%),目標在 2020 年度將電池材料等新能源部門營收提高至 1,000 億日圓、2025 年度進一步倍增至 2,000 億日圓的規模。

    三菱化學 2018 年 12 月 26 日宣布,日本國內外電動車、插電式油電混合車(PHV)、油電混合車(HV)市場呈現急速擴大,因此將擴增日本「電解液」產能,計畫將四日市事業所的電解液年產能自現行的 1.1 萬噸大幅擴產約 5 成至 1.6 萬噸。

    日韓企業紛紛擴增鋰離子電池關鍵材料產能

    旭化成(Asahi Kasei)3 月 14 日宣布,因鋰離子電池以電動車等車用需求為中心呈現急速增長,故決議投資 300 億日圓對位於日本滋賀縣守山市的守山製造所、以及位於北卡羅萊納州的美國工廠進行增產投資,增產鋰離子電池關鍵材料「分隔膜」,上述增產工程預計於 2021 年度上半年開始商轉,預估 2021 年度旭化成分隔膜全球年產能將擴增至約 15.5 億平方公尺、將較 2018 年度末提高 1 倍。

    住友化學(Sumitomo Chemical)日前也傳出將階段性提高南韓工廠產能,目標在 2021 年度將分隔膜總年產能(合併日本工廠產能計算)提高至 6 億平方公尺、將達現行的近 2 倍水準。

    全球第 2 大鋰離子電池關鍵材料「分隔膜」廠商南韓 SK Innovation 5 月 27 日表示,計畫在 2025 年結束前將分隔膜產能提高至現行的 5 倍,SK Innovation 社長金俊 27 日在首爾舉行的記者會上表示,「目標藉由大規模增產、搶當全球龍頭」。在全球分隔膜市場上,日本旭化成市佔約 2 成、位居首位,SK Innovation 市佔率超過 1 成居次。

    日本民間調查機構矢野經濟研究所公布調查報告指出,因中國自 2019 年起開始實施新能源車規範,加上 2019-2020 年期間日歐車廠將進行車輛電動化,提振車用鋰離子電池材料需求今後將持續擴大,預估 2020 年全球 4 大關鍵材料(正極材、負極材、電解液和分隔膜)市場規模(廠商出貨金額)將擴增至 281.46 億美元、將較 2017 年暴增 9 成(增加 91.3%)。

    (本文內容由 授權使用。首圖來源:)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • Chrome OS 80 將可以直接構建 Android 應用

    Chrome OS 80 將可以直接構建 Android 應用

      在近期 Android Dev Summit 上,Chrome OS 團隊宣布 Chrome OS 80 將使 Chromebook 可以直接構建 Android 應用。

      這項特性其實是在 Chrome OS 中引入 Android 應用側加載(sideloading),該功能的具體介紹來自一個非公開 bug 記錄以及相應的代碼更改,根據該記錄,Android 應用的側加載被帶到了 Chromebook 上的 Android 容器中。

      根據內部文件,具體開發時的操作是啟動 Crostini 容器時需要一個特殊命令(從 Chromebook 的命令行啟動 Linux 時),需要添加–enable-features = ArcAdbSideloading

      目前開發人員必須通過 USB 線將 Android 設備連接到 Chromebook,然後將其應用推送到設備上進行測試或使用 Chrome OS 開發人員模式,才能構建 Android 應用,但這兩種都不是理想的選擇。

      這項新特性對於使用 Android Studio 在 Chromebook 上構建其應用的 Android 開發人員來說,是極其方便的功能。具體來看,Chrome OS 80 將為 Android 開發人員添加選項,這樣可以直接在 Chrome OS 設備上安裝和測試其應用。

      消息來源:

      https://www.aboutchromebooks.com/news/chrome-os-80-to-bring-arc-sideloading-of-android-apps-to-chromebooks

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 區塊鏈成重要突破口,與幣圈無關卻與5G有關

    區塊鏈成重要突破口,與幣圈無關卻與5G有關

      幾乎是一夜之間,區塊鏈就成了更熱門的話題,此前,如果我們還在說“物聯網、大數據、雲計算、人工智能和 5G”,那麼,以後這一長串的背後可能就要加上“區塊鏈”。

      誠然,國家將區塊鏈當成戰略來發展,並“要把區塊鏈作為核心技術自主創新的重要突破口,明確主攻方向,加大投入力度,着力攻克一批關鍵核心技術,加快推動區塊鏈技術和產業創新發展”,這對整個區塊鏈產業都是巨大的利好,但是,這種利好卻實實在在與現在網絡上那些頂着區塊鏈帽子實際卻是在搞傳銷的“幣”們,毫無關係。如果有關係,那也一定是負面。

      在國內,區塊鏈技術佔據優勢的公司依然是 BAT,特別是阿里巴巴和螞蟻金服。在 2018 年的世界互聯網大會上,螞蟻金服“自主可控金融級商用區塊鏈平台”與“阿里雲 supET 工業互聯網平台”同時入選 15 項世界互聯網領先科技成果之列。前者的區塊鏈因素顯而易見,後者實際上也在由區塊鏈提供製造生產質量追溯和供應鏈管理服務。

      據全球知名智能信息服務機構科睿唯安公布信息,截至 2019 年 4 月 30 日,阿里巴巴以 290 件區塊鏈專利方案數量穩居全球第一。結合權威知識產權產業媒體 IPRdaily 過去兩年發布的“全球區塊鏈專利企業排行榜”显示,阿里巴巴申請的區塊鏈專利數量已經連續三年全球第一。2019 上半年全球區塊鏈企業發明專利排行榜(TOP100) 中,阿里巴巴以 322 件專利位列第一,中國平安以 274 件專利排名第二,Nchain 以 241 件專利排名第三。此外,百度( 7 名)、騰訊( 13 名)、京東( 14 名)、華為( 28 名)、獵豹( 40 名)、中國移動( 44 名)等均榜上有名。

      從上面的數據可以看出來,真正在區塊鏈的研究上佔據優勢且不斷應用的,沒有一個是“發幣”的,都是實實在在的在落地。

      當然了,也許與幣有關。此前有消息傳言,央行可能將在未來幾個月內正式推出國家支持的数字貨幣“DCEP”,初期將向中國工商銀行、中國建設銀行、中國銀行、中國農業銀行、阿里巴巴、騰訊以及銀聯七家機構發行。如果成行,那將是世界上是首個法定数字貨幣,意義深遠。

      在官方發布的新聞中,將區塊鏈發展方向定位為四個方面:

      1、要探索“區塊鏈+”在民生領域的運用,积極推動區塊鏈技術在教育、就業、養老、精準脫貧、醫療健康、商品防偽、食品安全、公益、社會救助等領域的應用,為人民群眾提供更加智能、更加便捷、更加優質的公共服務。

      2016 年,螞蟻金服聯合中國紅十字基金會等公益機構上線區塊鏈試驗項目,使捐款人可以追蹤善款的完整流轉情況。2017 年 3 月,支付寶愛心捐贈平台全面引入區塊鏈技術,所有捐贈數據上鏈。實現了實時賬目公示,有助於解決公益財務透明的“痛點”。螞蟻金服公益運用區塊鏈技術追蹤籌款,建立起第三方公示體系區塊鏈資金流公示,為公益機構進行數據統計、項目執行跟蹤提供便利。區塊鏈具有不可篡改的特性,任何寫入區塊鏈的記錄均不能更改,可以供公眾監督及審計。而“區塊鏈+公益”正是利用這一特性,發揮公眾賬本的價值,不論用戶是捐十塊、二十塊還是幾百塊,讓用戶所獻出的每一筆都記錄在區塊鏈上,有跡可循,持續追溯。

      2016 年 10 月,阿里與微軟、小蟻、法大大等合作開發“法鏈”,推出基於阿里雲平台的郵箱存證產品,通過法鏈上備份的电子郵件和雲服務,阿里將使中國法院能大規模採用数字證據郵件。

      2017 年 3 月,阿里巴巴與普華永道合作,打造可追溯的跨境食品供應鏈,用於跟蹤產品從生產者到消費者之間的整個流程。

      2017 年 8 月,阿里健康與江蘇常州市合作推出我國首個基於醫療場景的區塊鏈應用――“醫聯體+區塊鏈”試點項目。

      2017 年 10 月 11 日,螞蟻金服 CTO 程立在螞蟻金服金融科技開放峰會上首度披露未來的技術布局――“BASIC”戰略,其中的B對應的就是區塊鏈(Blockchain),同時,技術實驗室宣布開放區塊鏈技術,支持進口食品安全溯源、商品正品溯源等,第一個落地場景將是海外奶粉品牌的追蹤,先是產自澳洲、新西蘭的 26 個品牌的奶粉。2017 年 11 月 24 日,天貓國際宣布升級全球原產地溯源計劃,未來將覆蓋全球 63 個國家和地區,3700 個品類,14500 個海外品牌,也將向全行業開放,賦能整個行業。2018 年 2 月,菜鳥與天貓國際官方消息,已啟用區塊鏈技術跟蹤、上傳、查證跨境進口商品的物流全鏈路信息,涵蓋生產、運輸、通關、報檢、第三方檢驗等商品進口全流程,為每個跨境進口商品打上獨一無二的“身份證”,供消費者查詢驗證。

      騰訊旗下的微眾銀行於 2016 年 6 月開發出面向金融業的聯盟鏈雲服務 BaaS,並在 2017 年 1 月落地了第一個商業場景,即微黃金項目。微黃金是騰訊的一項在線黃金交易服務,用戶可以在微信低門檻靈活購買黃金,其背後正是由騰訊開發底層基礎架構的聯盟鏈。騰訊、工商銀行等多個節點共同參与記帳。2018 年 4 月底,騰訊發布第一款區塊鏈遊戲《一起來捉妖》,試圖通過區塊鏈技術保障了遊戲稀有內容投放的公開公平性,同時用戶可以將稀有妖怪上鏈永久保存。

      2017 年 12 月,沃爾瑪、京東、IBM、清華大學电子商務交易技術國家工程實驗室共同宣布成立安全食品區塊鏈溯源聯盟,運用區塊鏈技術搭建“京東區塊鏈防偽追溯開放平台”。該平台推出之後,用戶只需打開京東 APP 找到購物訂單,通過“一鍵溯源”或直接掃描產品溯源碼,即可獲取溯源信息。

      2018 年 10 月,百度正式發布自主研發的區塊鏈網絡系統——超級鏈。同時,宣布與海南省政府合作,區塊鏈實驗室及度鏈公司落戶海南,並推出“區塊鏈+大閘蟹溯源”應用。

      2、要推動區塊鏈底層技術服務和新型智慧城市建設相結合,探索在信息基礎設施、智慧交通、能源電力等領域的推廣應用,提升城市管理的智能化、精準化水平。

      5 月 22 日,上海、浙江、江蘇、安徽 4 地法院成立全國首個區域司法鏈,實現從起訴到執行全程上鏈,極大提高了訴訟效率和法院公信力,降低訴訟成本。最早引入區塊鏈技術的杭州互聯網法院,上鏈半年,其知識產權糾紛案件的調撤率上升至 95.3%。

      2017 年 5 月,阿里投資 Symbiont,該公司致力於利用區塊鏈技術打造一個發行和交易智能證券的平台。此外,深圳財富文化集團已經打造了一個文化產權交易的區塊鏈平台,並與螞蟻雙鏈打通,解決文化藝術品流通、交易和一系列金融服務。

      2017 年 11 月 8 日,阿里巴巴集團、螞蟻金服集團與雄安新區簽署了戰略合作協議,阿里巴巴與螞蟻金服將承建数字雄安區塊鏈實施平台。

      另外,國網電商公司已建成國家電網公司系統內首個司法級可信區塊鏈公共服務平台,作為唯一央企與北京互聯網法院“天平鏈”互信互通,掛牌工信部區塊鏈重點實驗室電力應用實驗基地,參与首個區塊鏈國家標準制定,實現了區塊鏈技術在電力積分通兌、光伏簽約、票據繳費、电子發票等多場景落地應用。

      3、要利用區塊鏈技術促進城市間在信息、資金、人才、徵信等方面更大規模的互聯互通,保障生產要素在區域內有序高效流動。

      在 9 月末舉行的雲棲大會·螞蟻區塊鏈生態峰會上,螞蟻金服集團副總裁蔣國飛透露,螞蟻區塊鏈已落地 40 多個應用。含括跨境支付、供應鏈金融、通用溯源、电子票據等多個行業領域。並於同期宣布了與全球最大的種子和農藥公司拜耳合作,應用方向進一步擴展。

      螞蟻金服 8 月份宣布,基於區塊鏈技術的供應鏈協作網絡——螞蟻區塊鏈“雙鏈通”全面升級開放。這一服務運用區塊鏈技術可解決供應鏈金融中的信任難題,同時讓小微商家也能享受高效便捷的金融服務。目前,這一模式已在成都率先應用。通過與成都商業銀行、成都中小企業融資擔保公司的合作,“雙鏈通”完成了供應鏈金融的全鏈路覆蓋。上鏈后,整個融資流轉過程清晰留痕、不可篡改,所有參与方都要通過“雙鏈通”進行身份核實和意願確認,数字簽名實時上鏈,杜絕了資金挪用等風險。

      4、要探索利用區塊鏈數據共享模式,實現政務數據跨部門、跨區域共同維護和利用,促進業務協同辦理,深化“最多跑一次”改革,為人民群眾帶來更好的政務服務體驗。

      3 月,杭州地鐵聯合支付寶、螞蟻區塊鏈推出了區塊鏈电子發票。全程手機操作,耗時不到 10 秒,上班族再也不用前往窗口排隊取票了。支付寶聯合雲南省相關部門,在醫療、教育等多個民生服務場景落地區塊鏈电子票據。從地鐵电子發票到法院訴訟案件,再到电子票據平台,螞蟻區塊鏈在過去近 3 年時間里,相繼落地了 40 多個場景。其中長三角佔比超過一半。“上鏈”后長三角人辦事效率大大提升,不少事項可享受“秒級”服務。

      我們看到,在 5G 時代,無差別計算能力的流通成本會大幅下降,無差別計算能力是 AI 最重要的勞動要素,而區塊鏈就是 AI 最重要的勞動要素。阿里巴巴達摩院在《2019 十大科技趨勢》中提到,5G 和區塊鏈是未來十年的發展方向。《福布斯》發布的《2020 十大科技趨勢》也提到,5G 和區塊鏈都將於 2020 年崛起,未來與人們日常生活相關的每一個行業都會被變革。還是那句話,“誰能把當前的技術和資源用到最充分,誰就是這個時代的最強者”,也許 5G 的流量充沛,正是區塊鏈的黃金歲月。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

  • 6000年前一隻狗的癌症,如何在今天席捲全球?

    6000年前一隻狗的癌症,如何在今天席捲全球?

      即使睿智如人類,強壯如猛獸,也無法躲過疾病的侵襲,癌症就是其中尤為嚴重的一種。

      時至今日,我們仍舊深陷於與癌症抗爭的泥潭中苦苦掙扎。目前已有的治療手段有手術、化學療法、放射線療法、癌症疫苗、免疫細胞療法等。但很遺憾,目前除了針對非實體瘤的治療方法效果卓著接近曙光,對大多數癌症仍舊是無計可施。

      究其原因,仍是我們對癌症不夠了解,或者說是無從下手。以人類短短百餘年的壽命極限來抗衡與進化同生的癌症,實在是有些蚍蜉撼樹的意味。癌症的偶然發生,以單一生命個體為單位進行研究,百餘年的度量難以拼接成完整的畫面。

      而今,一種至今已存在了 6000 余年的癌症為我們的研究提供了絕佳的條件。


    來源:Ernesto del Aguila III, NHGRI

      CTVT(canine transmissible venereal tumor),即犬類傳染性性病腫瘤。這種癌症最早起源於中亞地區,來自於某條“始祖犬”的生殖器細胞基因突變。隨後,伴隨犬類的交配,生生不息,如今已經幾乎遍布世界的每個角落,至今已有約 6000 年。

      來自 40 多個國家的聯合團隊,通過對來自 43 個國家的 546 個 CTVT 腫瘤樣本和 495 個 CTVT 腫瘤宿主的正常樣本進行了外顯子測序,構建出時間系統發育譜系。同時研究者們對 CTVT 的癌症突變特徵進行了分析,並由此識別出 CTVT 的高度環境特異性突變過程,以及中性遺傳漂變是癌症長期演化的主要特徵,相關的研究細節發表在 Science 雜誌上。

      對 CTVT 的研究,為人類在數千年的時間單位上更好地認識癌症進化上提供了絕佳的機會,這也將是人類未來戰勝癌症的重要參考。

      一、對癌症的認識

      癌症,又名惡性腫瘤,是指細胞不正常的增生,且這些增生的細胞可能隨淋巴或血液系統攻佔身體的每個角落。千萬年間,人們始終沒有放棄與癌症的抗爭,卻屢屢折戟沉沙。因此,癌症在很長時間內都被認為是無法治癒的疾病,神靈的詛咒。

      在人類身上,目前已知的癌症已經超過 100 種。2015 年,約有 880 萬人死於癌症,這幾乎佔到了全球死亡人數的六分之一,其中的 70% 發生在低收入和中等收入國家。

      癌症並非一種源於工業化的人造疾病,而是與演化如影隨形,共同塑造了生命。癌症的存在歷史可以追溯至上萬年,但直到近百年間,人們才開始真正地了解癌症。

      18 世紀,醫生藉助解剖刀開始了與癌症的正式交鋒——腫瘤切除治療。但癌症的複發與轉移,成為橫亘在醫生們面前的又一條門檻。

      那麼,究竟什麼才是癌症背後真正的力量呢?答案是基因

      事實上,癌症是一種依賴基因突變的慢性疾病。一般來說,同一種癌症在不同患者身上,甚至是同一患者的不同器官或組織中,都可能具有不同的基因型。癌症,似乎可以看做是某些邪惡基因隨機發生於宿主個體間的一種“寄生”。

      肉體總有終結之時,但癌症永生。當然,對於絕大多數不具有傳染性的癌症來說,只是在時間跨度下的眾多個體間的廣義永生。事實上,有極少數的癌症的確可以在生命個體間傳播,延續着自己的生命,完成永生。

      但值得一提的是,傳染性癌症區別於感染型癌症,並不是通過病毒感染誘發的。大多數病毒感染誘發的癌症,如人乳頭瘤病毒引起的宮頸癌、乙肝病毒引起的肝癌,都可以通過接種疫苗有效預防。

      二、古老的癌症

      對於大多數癌症來說,他們隨機的發生於單一個體,隨個體的壽命而發生、發展、終結。而其中的極少數癌症,可以在個體間進行傳播,就像“寄生”在宿主中完成自身的演化時間線,CTVT 就是其中一員。

      這種來源於犬類的癌症起源於中亞,遺傳信息穩定且高度相似。對於它開始的時間,研究者們尚存在爭議,一部分人認為約在 1.1 萬年前犬類的馴化時間點上,也有人認為發生於時間稍近的 6000 多年前。

      通過犬類之間的交配、甚至是舔舐,CTVT 在群體間進行傳播。每一顆癌細胞就像是種子,到達下一個宿主體內,等待合適的時機繼續傳播。

      隨着大航海時代的到來,人類的生活半徑增大,而犬類也跟隨人類開始了他們的遷移。時至今日,幾乎在每塊大陸上,都有 CTVT 的痕迹。

      而如今,它居然歪打正着地成為研究癌症的最佳手段,幫助人類追蹤癌症的演化,破解癌症的謎團。

      三、揭開千年疑團

      在此項研究中,研究者們對來自 43 個國家的 546 個 CTVT 腫瘤樣本和 495 個 CTVT 腫瘤宿主的正常樣本進行了外顯子測序,並構建出時間系統發育譜系。分析結果显示,CTVT 細胞大約在 6200 年前首次於亞洲出現,目前廣泛分佈的 CTVT 細胞的源頭可以追溯到約 1900 年前的印度。彼時 CTVT 開始產生亞型,並開始向歐洲、亞洲蔓延擴散。隨着大航海時代的到來,CTVT 的傳播也搭上了“順風船”,跟隨人類的足跡踏上更多的陸地。


    來源:Science

      隨後研究者們對 CTVT 的癌症突變特徵進行了分析,並由此識別出 CTVT 的高度環境特異性突變過程。同時,研究者發現了 5 個促進 CTVT 發生和傳播的早期驅動基因:SETD2,CDKN2A,MYC,PTEN 和 RB1。研究者也發現,CTVT 幾乎沒有晚期陽性選擇,解釋了中性遺傳漂變是癌症長期演化的主要特徵。

      殖民、全球化、同質化,共同作用造成了如今的 CTVT。而存活了數千年、從來不能滅亡的 CTVT,同時也像活化石、錄影帶一樣記錄了癌症的進化歷程。管中窺豹,可見一斑。

      對於 CTVT 來說,癌細胞似乎更像是一種獨立的生命體在不同的“宿主”間傳播,雖然來源不同,但卻可以和不同個體的免疫系統都相安無事。儘管目前並未發現可以在人體間傳染的癌症,但足以為器官移植敲響警鐘。如果捐贈者的器官中留有癌症的“種子”,對於接受器官移植的人來說很可能是一場可怕的災難。

      同時,CTVT 的中性進化也為現代癌症的治療提供思路。對於一些進程緩慢的癌症,似乎可以嘗試適應性療法,而非在癌細胞和宿主間,一定要斗個“你死我活”。

      如果承載生命的主體是遺傳物質,那麼毫無疑問,癌症從未死去。如果短期內無法戰勝,找到與它“同生”的方法或許並不是最壞的選擇。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 中國AI獨角獸完成全球競賽三連冠偉業:孫劍帶隊,曠視再刷榜

    中國AI獨角獸完成全球競賽三連冠偉業:孫劍帶隊,曠視再刷榜

      邊策 郭一璞 發自 凹非寺
      量子位 報道 公眾號 QbitAI

      全球 AI 競賽再奪 3 項世界第一,實現 COCO 重頭戲“物體檢測”三連冠。

      新歷史、新紀錄,榮耀屬於中國公司,展示的統治力堪比乒乓球。

      這就是 AI 獨角獸曠視科技,剛剛從全球 AI 頂會 ICCV 2019 傳來的捷報。

      而且 IPO 上市當前,無疑是既有實力的繼續展示。

      全球 AI 競賽再奪 3 項第一

      ICCV,國際計算機視覺大會,英文全稱 International Conference on Computer Vision。

      被譽為計算機視覺領域三大頂級會議之一,與 CVPR 和 ECCV 並列。

      ICCV 兩年舉辦一次,今年 10 月 27 日在韓國首爾開幕。

      不過剛剛開幕,中國力量就捷報頻傳,繼續展現在全球 AI 研發領域的潛力和實力。

      特別是在本屆 ICCV 的 COCO 挑戰賽上,曠視再度問鼎,拿下 COCO 物體檢測(Detection)、人體關鍵點(Keypoint)和全景分割(Panoptic)3 項第一。

      繼 2017、2018 年後再度奪冠,更是在最重要的“物體檢測”完成三連冠偉業,自 2015 年 COCO 開賽以來,前無古人,創下新紀錄。

      此外,曠視還獲得今年新設立的 COCO+Mapillary 挑戰賽的最佳論文獎(Best Paper Award),原因是“最具創新性的算法”。

      COCO 是 ICCV 2019 的重頭戲,也是 AI 視覺領域最具影響力的通用物體檢測挑戰賽。

      今年的 COCO 挑戰賽與往年不同,加入了新的規則:

    首先、參加者必須提交一份技術報告,該報告將替代先前要求的簡短描述。只有與報告一起提交的材料才會被考慮參賽,並被放入 COCO 排行榜中。

    其次,今年的每個挑戰賽都將設立兩個不同的獎項:最佳結果獎和最具創新獎。最具創新獎根據根據參賽作品的創新而非最佳成績來評定,最終由 COCO 獎項委員會決定,獲獎團隊將受邀參加 Workshop。

    最後,今年的大會針對所有挑戰提供最具創新性和成功解決方案的最佳論文獎。獲獎者將由研討會組委會確定。

      物體檢測任務是讓算法輸出邊界框輸出或實例分割,自動駕駛、醫療影像識別中都會用到。另外在 2019 年的挑戰賽中,只有具有目標分割輸出的檢測任務會被重點介紹。

      COCO 全景分割任務目的是生成豐富而完整的連貫場景分割,這是自動駕駛或增強現實等實際應用的中一項重要技術。

      人體關鍵點檢測是在不提供人位置的情況下,定位並返回人體各部位關鍵點坐標位置。關鍵點定位了頭、肩、肘、手、臀、膝、腳等部位,可以用於人的行為識別,對於安防技術有重大意義。

      三連冠偉業

      今年的 MS COCO 總共 7 項比賽,除了曠視的 3 項冠軍,香港中文大學-商湯科技聯合實驗室和南洋理工大學團隊也在 Object Detection 比賽中拿到了不含額外數據集的第一名。

      可以說中國軍團延續傳統,繼續在全球 AI 競技中展現實力。

      而且 COCO 中最被看重的“物體檢測”比賽,孫劍和其帶隊的中國軍團,更是實現了垄斷級的統治力。

      物體檢測項目,從 2015 年第一屆就存在,此後一直延續了下來。

      在第一屆比賽中,孫劍帶隊的 MSRA 團隊斬獲冠軍,成員包括何愷明(現 FAIR)、任少卿(現 Momenta)、代季峰(現商湯)和張祥雨(現曠視),所用的算法,是何愷明和 RBG 大神第一次合作的 Faster R-CNN。

      不過 2016 年,冠軍被谷歌研究院的G-RMI 隊拿下,只是所用的算法依然是 Faster R-CNN。

      2015 年第一屆 MS COCO 大賽中除了物體檢測,還有個生成圖片說明(Captioning Challenge)項目,當時奪冠的谷歌團隊,與人類 baseline 相比依然差了一大截,這個比賽項目也沒能繼續下去。

      在 2016 年,物體檢測之外的比賽項目變成了人體關鍵點檢測,當時奪冠的團隊來自 CMU。

      而從 2017 年開始,COCO 的各項比賽,就真正進入了中國時間——甚至可以更具體說“曠視時間”。

      這家中國 AI 獨角獸在孫劍加盟擔當研究院院長后,如虎添翼,在 COCO 競賽中展現出的實力,就像中國乒乓球、女排在奧運會展現的一樣。

      2017 年,MS COCO 的 6 個比賽項目中,曠視拿下了邊界檢測(Detection: Bounding Box)、人體關鍵點檢測(Keypoints)和地點實例分割(Places Instance Segmentation)3 個項目的冠軍,以及檢測分割(Detection: Segmentation)的亞軍。

      而在 2018 年的 6 個項目中,曠視拿下了物體檢測(Detection)、全景分割(Panoptic)、人體關鍵點檢測(Keypoints)和 Mapillary Panoptic4 個項目的冠軍。

      另外的兩個項目 DensePose 和 Mapillary 街景檢測則分別由北京郵電大學自動化學院模式識別與測控技術實驗室(BUTP-PRIV)和滴滴獲得——這一整屆比賽的冠軍都被中國團隊包了。

      所以算下來,曠視已經在三年 MS COCO 的比賽上拿到了累計 10 個冠軍。

      最重頭的“物體檢測”,更是完成三連冠偉業,前無古人。

      更加值得一提的是,之前我們也揭秘過,曠視在 COCO 上的統治力,去往年開始就啟用大牛老將帶實習生參賽的機制,於是諸多名不見經傳的本科實習生,早早就成為了 AI 世界冠軍。

      而且刷榜奪冠,不僅是鍛煉隊伍,而且對曠視自研的 Brain++,也是一次次最佳說明。

      在今年 ICCV 奪冠后,曠視研究院院長孫劍再次感謝曠視算法工具平台 Brain++,稱一連串成績的取得,離不開背後強大的 Brain++。

      曠視介紹,一方面 Brain++ 具備多機訓練方案,支持完備的底層算法,確保算法的高效實現與快速驗證。

      比如,在 COCO Detection 任務中,曠視重新設計了 RPN 匹配策略和 Proposal 採樣策略,使用兩階段檢測器即可直接獲得很好的高 IoU 檢測結果,甚至超過了使用更多階段的 Cascade R-CNN,最終大幅領先,取得 test-challenge 52.5 的冠軍成績。

      另一方面 Brain++ 針對計算機視覺定製優化,適合工業界的產品開發,為競賽技術的應用轉化鋪平道路。

      還集成了新一代 AutoML 技術,降低算法試錯成本,實現技術創新和產品落地齊頭並進。Brain++從算法設計、算法框架和算法平台三個方面為 AI 競賽保駕護航。

      在剛剛舉行的烏鎮世界互聯網大會上,Brain++還為曠視斬獲了“世界互聯網領先科技成果”。

      於是也有更多注意力開始關注起這個基礎算法研發平台,曠視聯合創始人及 CTO 唐文斌還比喻說,如果說曠視各種各樣的算法是“雞蛋”,那 Brain++ 就是出產“所有雞蛋”的“母雞”。

      總之,Brain++可以視為曠視面向 AI 時代的生產工具,而再配以優秀的人才,連續在全球 AI 競技中奪魁,自然也不是意料之外。

      One more thing

      最後,新冠軍、新紀錄和三連冠偉業之外,今年 COCO 的冠軍對曠視而言還有更多意義。

      一方面,這家 AI 獨角獸已經向港交所提交了招股書,IPO 上市只是時間問題,也將成為 AI 創業上市第一股。

      另一方面,就在不久前,曠視也突發遭遇偷襲,被美國列入了“實體名單”,成為“川普優選”的又一家公司。

      因為衝刺上市當前,還引發了更多關注。

      但 Brain++ 獲國家認證,COCO 比賽三連冠,毫無疑問就是最好回應。

      還是那句話,如果美國拉黑了你,不要悲傷,不要氣餒。

      猝不及防的日子,最好的回擊就是業績和人心。

      並且 ICCV 2019 這才剛剛開幕,聽說曠視的三連冠,還不是中國 AI 新榮譽的全部。

      讓我們保持關注,繼續期待~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • JVM 問題排查和性能優化常用的 JDK 工具

    JVM 問題排查和性能優化常用的 JDK 工具

    JDK 提供了一系列用於監控、診斷 Java 進程的工具,它們在 JDK 安裝目錄的 bin 目錄下,有 jps、jcmd、jstack、jinfo、jmap 等。其中jmc、jconsole、jvisualvm 是 GUI 工具,其他大部分都是命令行工具。

    cd $JAVA_HOME/bin
    ls

    本篇只是個入門介紹,不涉及深入分析。每一個工具都有它專門的作用,掌握使用方法只是很簡單的入門階段,更重要的是根據工具得到的信息去分析系統存在的問題以及性能瓶頸,每一個工具的使用和分析都可以單獨成文。

    jps

    如果你用過 Linux,那肯定熟悉 ps 命令,用來查看進程列表的。jps 就好比是 ps 命令的子集,它查詢的是當前用戶下已經啟動的 Java 進程。這是進行線上問題排查的大門鑰匙,有了它才能下手後面的動作。

    下面是 jps 的幫助文檔

    usage: jps [-help]
           jps [-q] [-mlvV] [<hostid>]
    
    Definitions:
        <hostid>:      <hostname>[:<port>]

    一般的用法是 jps -l,前面一列显示 pid,後面一列显示進程名稱。

    還可以用下列參數查看更具體的 Java 進程信息,用法為 jps -lv

    jstack

    查看 Java 進程內當前時刻的線程快照,也就是每條線程正在執行的方法棧情況,用於定位線程停頓、死鎖等長時間等待的問題。

    以下是 jstack 的幫助文檔。

    Usage:
        jstack [-l] <pid>
            (to connect to running process)
        jstack -F [-m] [-l] <pid>
            (to connect to a hung process)
        jstack [-m] [-l] <executable> <core>
            (to connect to a core file)
        jstack [-m] [-l] [server_id@]<remote server IP or hostname>
            (to connect to a remote debug server)
    
    Options:
        -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
        -m  to print both java and native frames (mixed mode)
        -l  long listing. Prints additional information about locks
        -h or -help to print this help message
    

    最常用的就是 jstack -pid 或者 jstack -l pid,打印線程狀態、棧使用情況。

    如果是線上查看不方便的話,可以用命令 jstack -l pid > stack.log,輸出到文件中下載到本地查看。

    jstack -m pid,打印 Java 和 Native 棧信息

    如果 -l 和 -m 都不起作用的時候,可以使用 java -F pid 強制 dump。

    jinfo

    它的主要作用是查看 JVM 配置參數,還可以動態設置部分參數值。jinfo 使用時需要 attach 到目標 JVM 上。關於 attach jvm 可以點擊查看

    使用 jinfo -h查看幫助文檔

    Usage:
        jinfo [option] <pid>
            (to connect to running process)
        jinfo [option] <executable <core>
            (to connect to a core file)
        jinfo [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        -flag <name>         to print the value of the named VM flag
        -flag [+|-]<name>    to enable or disable the named VM flag
        -flag <name>=<value> to set the named VM flag to the given value
        -flags               to print VM flags
        -sysprops            to print Java system properties
        <no option>          to print both of the above
        -h | -help           to print this help message

    jinfo -flags pid

    查看 JVM 參數,其中 Non-default VM flags 是虛擬機默認設置的參數,Command line 是用戶指定的參數,比如命令行啟動 jar 包的時候加上的參數。

    jinfo -flag 參數名 pid

    可以查看指定參數的值,比如查看堆的最大值(-XX:MaxHeapSize 也就是 -Xmx ):

    jinfo -flag MaxHeapSize 92041
    
    -XX:MaxHeapSize=20971520

    jinfo -sysprops pid

    查看系統參數

    jinfo pid

    查看 jvm 參數和系統參數

    以上信息,如果我們用過 visualVM 等監控工具,一定非常熟悉。另外,我之前做過一個 ,也實現了這個功能。

    另外,還可以修改部分參數值。

    jinfo -flag [+|-] pid

    jinfo -flag = pid

    可以修改部分 JVM 參數。

    前者可以修改布爾值參數,比如開啟簡單 GC 日誌

    jinfo -flag +PrintGC 92041

    後者是設置非布爾值參數的,比如設置 HeapDumpPath

    jinfo -flag HeapDumpPath=/users/fengzheng/jvmlog

    哪些參數是允許動態修改的呢,用下面這個命令可以查看

    #Linux 和 Mac 
    java -XX:+PrintFlagsInitial | grep manageable
    
    #windows
    java -XX:+PrintFlagsInitial | findstr manageable

    jmap

    jmap 查看給定進程、核心文件、遠程調試服務器的共享對象內存映射和堆內存細節的工具,可查看堆使用情況、堆內對象直方圖、加載類、生成堆快照等。

    Usage:
        jmap [option] <pid>
            (to connect to running process)
        jmap [option] <executable <core>
            (to connect to a core file)
        jmap [option] [server_id@]<remote server IP or hostname>
            (to connect to remote debug server)
    
    where <option> is one of:
        <none>               to print same info as Solaris pmap
        -heap                to print java heap summary
        -histo[:live]        to print histogram of java object heap; if the "live"
                             suboption is specified, only count live objects
        -clstats             to print class loader statistics
        -finalizerinfo       to print information on objects awaiting finalization
        -dump:<dump-options> to dump java heap in hprof binary format
                             dump-options:
                               live         dump only live objects; if not specified,
                                            all objects in the heap are dumped.
                               format=b     binary format
                               file=<file>  dump heap to <file>
                             Example: jmap -dump:live,format=b,file=heap.bin <pid>
        -F                   force. Use with -dump:<dump-options> <pid> or -histo
                             to force a heap dump or histogram when <pid> does not
                             respond. The "live" suboption is not supported
                             in this mode.
        -h | -help           to print this help message
        -J<flag>             to pass <flag> directly to the runtime system

    jmap -heap pid

    打印 JVM 堆概要信息,包括堆配置、新生代、老生代信息

    jmap -histo pid

    打印類的直方圖,也就是各個類實例的個數和空間佔用情況。

    如果加 :live,jamp -histo:live pid 則只打印活動類的信息。這個命令會出發 GC 動作,會導致 JVM 停頓,所以在線上環境要慎用。

    jmap -dump

    dump 當前 JVM 堆,一般用法如下:

    #dump 所有對象在堆中的分佈情況
    jmap -dump:format=b,file=/Users/fengzheng/jvmlog/jamp_dump.hprof 95463
    
    #加:live 參數 dump 存活對象在隊中的分佈情況
    jmap -dump:live,format=b,file=/Users/fengzheng/jvmlog/jamp_dump.hprof 95463

    之後再用堆分析工具,比如 visualVM、JProfile、MAT 等進行分析。和我們設置

    -XX:+HeapDumpOnOutOfMemoryError 參數后,在發生 OOM 的時候 dump 的堆信息是一樣的。

    注意,dump 的過程會比較慢,在這個過程中會發生 JVM 停頓,而且在使用 :live 參數后,會觸發 GC 操作。

    jmap -clstats pid

    Java 類加載器(ClassLoader)信息,包括加載器名稱、已加載類個數、佔用空間、父加載器、是否存活、類型信息。

    jmap -finalizerinfo pid

    查看等待被回收的對象。

    jstat

    jstat 主要用來通過垃圾回收相關信息來判斷 JVM 性能問題,也可以查看類加載、編譯的情況,主要的用法是通過持續的固定時間間隔的輸出來觀察。比如每 3 秒打印一次 GC 回收次數,連續打印 10 次,通過動態的變化來觀察 GC 是否過於密集。

    下面是 jstat 的幫助手冊。

    Usage: jstat -help|-options
           jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
    
    Definitions:
      <option>      An option reported by the -options option
      <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                         <lvmid>[@<hostname>[:<port>]]
                    Where <lvmid> is the local vm identifier for the target
                    Java virtual machine, typically a process id; <hostname> is
                    the name of the host running the target Java virtual machine;
                    and <port> is the port number for the rmiregistry on the
                    target host. See the jvmstat documentation for a more complete
                    description of the Virtual Machine Identifier.
      <lines>       Number of samples between header lines.
      <interval>    Sampling interval. The following forms are allowed:
                        <n>["ms"|"s"]
                    Where <n> is an integer and the suffix specifies the units as 
                    milliseconds("ms") or seconds("s"). The default units are "ms".
      <count>       Number of samples to take before terminating.
      -J<flag>      Pass <flag> directly to the runtime system.

    通過 jstat -options 可以看到 jstat 支持查看哪些信息。

    $ jstat -options
    -class  #類加載情況 加載個數和空間使用
    -compiler #即時編譯器信息
    -gc  # GC情況 包括 young gc、full gc 次數、時間等
    -gccapacity #年輕代、老年代的使用情況
    -gccause #GC 統計信息和回收原因
    -gcmetacapacity #显示有關metaspace大小的統計信息
    -gcnew #新生代 GC 統計
    -gcnewcapacity #新生代內存統計
    -gcold #老年代 GC 統計
    -gcoldcapacity #老年代內存使用情況
    -gcutil #GC 匯總信息
    -printcompilation #編譯方法統計

    上述這些大多數可以對應到 visualVM 的這一部分显示

    示例用法,如下是打印 5301 進程下的垃圾回收情況,-h 3 表示每 3 行輸出一次標題信息,3s 5 表示每 3s 輸出一次,一共輸出 5 次

    jstat -gcutil -h 3 5301 3s 5

    最後輸出的內容如下:

    jstat -gcutil -h 3 5301 3s 5
      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.90  35.29  94.96  94.08     34   12.675     3    1.946   14.621
      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
     99.92   0.00  11.94  35.29  94.96  94.08     34   12.675     3    1.946   14.621
     99.92   0.00  11.94  35.29  94.96  94.08     34   12.675     3    1.946   14.621

    jcmd

    jcmd 會將命令發送給 JVM。這些命令包括用於控制 Java Flight Recording(飛行記錄)、診斷命令等。 必須運行在 JVM 本地,不能遠程使用,並且必須用 JVM 啟動用戶執行。

    通過 jps 命令找到一個 JVM 進程,然後使用下面的代碼可以看到 jcmd 支持的命令

    #進程 5173 
    jcmd 5173 help 
    
    5173:
    The following commands are available:
    JFR.stop
    JFR.start
    JFR.dump
    JFR.check
    VM.native_memory
    VM.check_commercial_features
    VM.unlock_commercial_features
    ManagementAgent.stop
    ManagementAgent.start_local
    ManagementAgent.start
    GC.rotate_log
    Thread.print
    GC.class_stats
    GC.class_histogram
    GC.heap_dump
    GC.run_finalization
    GC.run
    VM.uptime
    VM.flags
    VM.system_properties
    VM.command_line
    VM.version
    help

    基本包含了問題排查的常用命令,並且和上面介紹的幾個工具有部分重合。

    通過命令 jcmd 5173 help GC.heap_dump 可以查詢到 GC.heap_dump 命令的使用方法,其他命令都可以通過這個方法找到使用說明

    jcmd 5173 help GC.heap_dump
    5173:
    GC.heap_dump
    Generate a HPROF format dump of the Java heap.
    
    Impact: High: Depends on Java heap size and content. Request a full GC unless the '-all' option is specified.
    
    Permission: java.lang.management.ManagementPermission(monitor)
    
    Syntax : GC.heap_dump [options] <filename>
    
    Arguments:
        filename :  Name of the dump file (STRING, no default value)
    
    Options: (options must be specified using the <key> or <key>=<value> syntax)
        -all : [optional] Dump all objects, including unreachable objects (BOOLEAN, false)

    然後通過如下代碼就可以 dump 堆信息下來了,和 jmap -dump 的作用一樣

    jcmd 5173 GC.heap_dump /Users/fengzheng/jvmlog/jcmd_heap_dump.hprof

    拋磚引玉就到此了,之後會對 jinfo、jmap、jstack、jstat、jcmd 做詳細說明,記得關注啊。

    相關閱讀:

    歡迎關注,不定期更新本系列和其他文章
    古時的風箏 ,進入公眾號可以加入交流群

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Python 命令行之旅:深入 click 之子命令篇

    Python 命令行之旅:深入 click 之子命令篇

    作者:HelloGitHub-Prodesire

    HelloGitHub 的《講解開源項目》系列,項目地址:https://github.com/HelloGitHub-Team/Article

    一、前言

    在上兩篇文章中,我們介紹了 click 中的”參數“和“選項”,本文將繼續深入了解 click,着重講解它的“命令”和”組“。

    本系列文章默認使用 Python 3 作為解釋器進行講解。
    若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~

    二、命令和組

    Click 中非常重要的特性就是任意嵌套命令行工具的概念,通過 和 (實際上是 )來實現。

    所謂命令組就是若干個命令(或叫子命令)的集合,也成為多命令。

    2.1 回調調用

    對於一個普通的命令來說,回調發生在命令被執行的時候。如果這個程序的實現中只有命令,那麼回調總是會被觸發,就像我們在上一篇文章中舉出的所有示例一樣。不過像 --help 這類選項則會阻止進入回調。

    對於組和多個子命令來說,情況略有不同。回調通常發生在子命令被執行的時候:

    @click.group()
    @click.option('--debug/--no-debug', default=False)
    def cli(debug):
        click.echo('Debug mode is %s' % ('on' if debug else 'off'))
    
    @cli.command()  # @cli, not @click!
    def sync():
        click.echo('Syncing')

    執行效果如下:

    Usage: tool.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --debug / --no-debug
      --help                Show this message and exit.
    
    Commands:
      sync
    
    $ tool.py --debug sync
    Debug mode is on
    Syncing

    在上面的示例中,我們將函數 cli 定義為一個組,把函數 sync 定義為這個組內的子命令。當我們調用 tool.py --debug sync 命令時,會依次觸發 clisync 的處理邏輯(也就是命令的回調)。

    2.2 嵌套處理和上下文

    從上面的例子可以看到,命令組 cli 接收的參數和子命令 sync 彼此獨立。但是有時我們希望在子命令中能獲取到命令組的參數,這就可以用 來實現。

    每當命令被調用時,click 會創建新的上下文,並鏈接到父上下文。通常,我們是看不到上下文信息的。但我們可以通過 裝飾器來顯式讓 click 傳遞上下文,此變量會作為第一個參數進行傳遞。

    @click.group()
    @click.option('--debug/--no-debug', default=False)
    @click.pass_context
    def cli(ctx, debug):
        # 確保 ctx.obj 存在並且是個 dict。 (以防 `cli()` 指定 obj 為其他類型
        ctx.ensure_object(dict)
    
        ctx.obj['DEBUG'] = debug
    
    @cli.command()
    @click.pass_context
    def sync(ctx):
        click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
    
    if __name__ == '__main__':
        cli(obj={})

    在上面的示例中:

    • 通過為命令組 cli 和子命令 sync 指定裝飾器 click.pass_context,兩個函數的第一個參數都是 ctx 上下文
    • 在命令組 cli 中,給上下文的 obj 變量(字典)賦值
    • 在子命令 sync 中通過 ctx.obj['DEBUG'] 獲得上一步的參數
    • 通過這種方式完成了從命令組到子命令的參數傳遞

    2.3 不使用命令來調用命令組

    默認情況下,調用子命令的時候才會調用命令組。而有時你可能想直接調用命令組,通過指定 click.groupinvoke_without_command=True 來實現:

    @click.group(invoke_without_command=True)
    @click.pass_context
    def cli(ctx):
        if ctx.invoked_subcommand is None:
            click.echo('I was invoked without subcommand')
        else:
            click.echo('I am about to invoke %s' % ctx.invoked_subcommand)
    
    @cli.command()
    def sync():
        click.echo('The subcommand')

    調用命令有:

    $ tool
    I was invoked without subcommand
    $ tool sync
    I am about to invoke sync
    The subcommand

    在上面的示例中,通過 ctx.invoked_subcommand 來判斷是否由子命令觸發,針對兩種情況打印日誌。

    2.4 自定義命令組/多命令

    除了使用 來定義命令組外,你還可以自定義命令組(也就是多命令),這樣你就可以延遲加載子命令,這會很有用。

    自定義多命令需要實現 list_commandsget_command 方法:

    import click
    import os
    
    plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
    
    class MyCLI(click.MultiCommand):
    
        def list_commands(self, ctx):
            rv = []  # 命令名稱列表
            for filename in os.listdir(plugin_folder):
                if filename.endswith('.py'):
                    rv.append(filename[:-3])
            rv.sort()
            return rv
    
        def get_command(self, ctx, name):
            ns = {}
            fn = os.path.join(plugin_folder, name + '.py')  # 命令對應的 Python 文件
            with open(fn) as f:
                code = compile(f.read(), fn, 'exec')
                eval(code, ns, ns)
            return ns['cli']
    
    cli = MyCLI(help='This tool\'s subcommands are loaded from a '
                'plugin folder dynamically.')
    
    # 等價方式是通過 click.command 裝飾器,指定 cls=MyCLI
    # @click.command(cls=MyCLI)
    # def cli():
    #     pass
    
    if __name__ == '__main__':
        cli()

    2.5 合併命令組/多命令

    當有多個命令組,每個命令組中有一些命令,你想把所有的命令合併在一個集合中時,click.CommandCollection 就派上了用場:

    
    @click.group()
    def cli1():
        pass
    
    @cli1.command()
    def cmd1():
        """Command on cli1"""
    
    @click.group()
    def cli2():
        pass
    
    @cli2.command()
    def cmd2():
        """Command on cli2"""
    
    cli = click.CommandCollection(sources=[cli1, cli2])
    
    if __name__ == '__main__':
        cli()

    調用命令有:

    $ cli --help
    Usage: cli [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      cmd1  Command on cli1
      cmd2  Command on cli2

    從上面的示例可以看出,cmd1cmd2 分別屬於 cli1cli2,通過 click.CommandCollection 可以將這些子命令合併在一起,將其能力提供個同一個命令程序。

    Tips:如果多個命令組中定義了同樣的子命令,那麼取第一個命令組中的子命令。

    2.6 鏈式命令組/多命令

    有時單級子命令可能滿足不了你的需求,你甚至希望能有多級子命令。典型地,setuptools 包中就支持多級/鏈式子命令: setup.py sdist bdist_wheel upload。在 click 3.0 之後,實現鏈式命令組變得非常簡單,只需在 click.group 中指定 chain=True

    @click.group(chain=True)
    def cli():
        pass
    
    
    @cli.command('sdist')
    def sdist():
        click.echo('sdist called')
    
    
    @cli.command('bdist_wheel')
    def bdist_wheel():
        click.echo('bdist_wheel called')

    調用命令則有:

    $ setup.py sdist bdist_wheel
    sdist called
    bdist_wheel called

    2.7 命令組/多命令管道

    鏈式命令組中一個常見的場景就是實現管道,這樣在上一個命令處理好后,可將結果傳給下一個命令處理。

    實現命令組管道的要點是讓每個命令返回一個處理函數,然後編寫一個總的管道調度函數(並由 MultiCommand.resultcallback() 裝飾):

    @click.group(chain=True, invoke_without_command=True)
    @click.option('-i', '--input', type=click.File('r'))
    def cli(input):
        pass
    
    @cli.resultcallback()
    def process_pipeline(processors, input):
        iterator = (x.rstrip('\r\n') for x in input)
        for processor in processors:
            iterator = processor(iterator)
        for item in iterator:
            click.echo(item)
    
    @cli.command('uppercase')
    def make_uppercase():
        def processor(iterator):
            for line in iterator:
                yield line.upper()
        return processor
    
    @cli.command('lowercase')
    def make_lowercase():
        def processor(iterator):
            for line in iterator:
                yield line.lower()
        return processor
    
    @cli.command('strip')
    def make_strip():
        def processor(iterator):
            for line in iterator:
                yield line.strip()
        return processor

    在上面的示例中:

    • cli 定義為了鏈式命令組,並且指定 invoke_without_command=True,也就意味着可以不傳子命令來觸發命令組
    • 定義了三個命令處理函數,分別對應 uppercaselowercasestrip 命令
    • 在管道調度函數 process_pipeline 中,將輸入 input 變成生成器,然後調用處理函數(實際輸入幾個命令,就有幾個處理函數)進行處理

    2.8 覆蓋默認值

    默認情況下,參數的默認值是從通過裝飾器參數 default 定義。我們還可以通過 Context.default_map 上下文字典來覆蓋默認值:

    @click.group()
    def cli():
        pass
    
    @cli.command()
    @click.option('--port', default=8000)
    def runserver(port):
        click.echo('Serving on http://127.0.0.1:%d/' % port)
    
    if __name__ == '__main__':
        cli(default_map={
            'runserver': {
                'port': 5000
            }
        })

    在上面的示例中,通過在 cli 中指定 default_map 變可覆蓋命令(一級鍵)的選項(二級鍵)默認值(二級鍵的值)。

    我們還可以在 click.group 中指定 context_settings 來達到同樣的目的:

    
    CONTEXT_SETTINGS = dict(
        default_map={'runserver': {'port': 5000}}
    )
    
    @click.group(context_settings=CONTEXT_SETTINGS)
    def cli():
        pass
    
    @cli.command()
    @click.option('--port', default=8000)
    def runserver(port):
        click.echo('Serving on http://127.0.0.1:%d/' % port)
    
    if __name__ == '__main__':
        cli()

    調用命令則有:

    $ cli runserver
    Serving on http://127.0.0.1:5000/

    三、總結

    本文首先介紹了命令的回調調用、上下文,再進一步介紹命令組的自定義、合併、鏈接、管道等功能,了解到了 click 的強大。而命令組中更加高階的能力()則可看官方文檔進一步了解。

    我們通過介紹 click 的參數、選項和命令已經能夠完全實現命令行程序的所有功能。而 click 還為我們提供了許多錦上添花的功能,比如實用工具、參數自動補全等,我們將在下節詳細介紹。

    『講解開源項目系列』——讓對開源項目感興趣的人不再畏懼、讓開源項目的發起者不再孤單。跟着我們的文章,你會發現編程的樂趣、使用和發現參与開源項目如此簡單。歡迎留言聯繫我們、加入我們,讓更多人愛上開源、貢獻開源~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!