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 是電子產業重要的元件?

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

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

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

YAMAHA 與 Gogoro 合作首款電動速克達 EC-05 於 8 月上市,售價 99,800 元

台灣山葉(YAMAHA)和 Gogoro 在 2018 年宣布展開合作之後,不少消費者都好奇兩家公司會擦出什麼樣的火花。YAMAHA 終於在 6 月 27 日正式發表電動速克達 EC-05,也是雙方合作的第一款機車。

EC-05 採用 Gogoro 市售車種的平台架構,搭配 YAMAHA 的原創設計,未來也會掛上 YAMAHA 的品牌。EC-05 會以換電為動力來源,支援 Gogoro 旗下的換電站 GoStation,不過車主在購車後需要另行和 Gogoro 簽約購買換電服務。新款的電動速克達由 Gogoro 負責製造,並透過 YAMAHA 的通路進行銷售。

EC-05 的動力系統是 G2 鋁合金水冷永磁同步馬達(S-Version),配合 MOSFET 水冷馬達控制器。安全極速達到時速 90 公里,靜止加速到時速 50 公里僅需 3.9 秒。空車重量為 106 公斤,加上電池則為 126 公斤,擁有 25L 的置物空間。EC-05 可以連結智慧型手機和 Apple Watch,並使用 NFC 和藍牙進行連結。

EC-05 的電池位於車身當中,座墊底層結構、置物箱的開口部位與收納空間都和 Gogoro 現有車種相同,不過重塑車身線條的設計風格。里程表則根據 Gogoro 原有的元件進行調整,略為提高並向前方移動,減少騎乘者所需的視線移動。坐墊結構與材質沿用 Gogoro 的現行車種,不過座墊後方略為墊高,提供加速時的止滑和支撐。

EC-05 的電池位於車身內部,擁有 25L 的置物空間。

EC-05 的里程表和 Gogoro 的車種類似,不過略為提高並向前方移動。

EC-05 的頭燈。

EC-05 的尾燈。

YAMAHA 表示,EC-05 能夠幫助擴展台灣市場的產品線,不僅回應多樣化顧客的需求,也向電動車領域跨出一步。YAMAHA 指出 EC-05 將為 YAMAHA 未來的電動機車家族打下基礎,似乎暗示未來會推出更多與 Gogoro 合作的車款。YAMAHA 台灣總經理小川真司表示,YAMAHA 日本母公司與其他大廠合作的機車聯盟目前只限於日本國內的市場,因此並不會影響台灣子公司和 Gogoro 的合作。

小川真司認為雖然 EC-05 內部與 Gogoro 的車種相同,但 YAMAHA 的風格與 Gogoro 不同,而且雙方的消費族群也不一樣,可以給不同生活方式的族群不同的選擇。此外,YAMAHA 在台灣市場已經深耕多年,有更多的經銷商夥伴,無論是銷售或保養都能更貼近消費者。YAMAHA 對 EC-05 頗具信心,喊出了一年 2 萬台的銷售目標。

EC-05 提供藍灰色、深黑色、深藍灰色和白銀色 4 種顏色讓消費者選擇,定價為台幣 99,800 元,補助最多的桃園市汰換二行程機車換購電動機車補助 33,000 元,因此最低台幣 66,800 元起。預計將在 7 月 1 日開放預購,8 月 1 日正式上市。YAMAHA 將在台北、台中和高雄展開 EC-05 的巡迴賞車活動,活動期間參與的消費者將有機會抽中電動速克達 EC-05。

EC-05 共推出藍灰色、深黑色、深藍灰色和白銀色四款顏色。

(合作媒體:。圖片來源:)

延伸閱讀:

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

豐田衝刺燃料電池車,傳產能將擴增至 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 是電子產業重要的元件?

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

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

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

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

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

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

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

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

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

小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門

人生苦短,我用 Python

前文傳送門:

Docker 基礎

首先說一件事情,就在本文寫作前一天,Mirantis 這家公司宣布收購了 Docker 的企業業務和團隊。並且在官網上也掛出了相關的文字。

沒別的意思,相關的新聞通告一下。

Docker 官網地址:

什麼是 Docker ?

emmmmm,說實話,Docker 是什麼並不好說,下面通過四點大致解釋下 Docker 到底是個什麼東西。

  1. Docker 是全球領先的軟件容器平台。
  2. Docker使用Google公司推出的Go語言進行開發實現,基於Linux內核的cgroup,namespace,以及AUFS類的UnionFS等技術,對進程進行封裝隔離,屬於操作系統層面的虛擬化技術。
  3. 由於隔離的進程獨立於宿主和其它的隔離的進程,因此也稱其為容器。Docke最初實現是基於LXC。
  4. 用戶可以方便地創建和使用容器,把自己的應用放入容器。

Docker 容器的特點:

  1. 輕量化:在一台機器上運行的多個Docker容器可以共享這台機器的操作系統內核;它們能夠迅速啟動,只需佔用很少的計算和內存資源。鏡像是通過文件系統層進行構造的,並共享一些公共文件。這樣就能盡量降低磁盤用量,並能更快地下載鏡像。
  2. 標準化:Docker容器基於開放式標準,能夠在所有主流Linux版本、Microsoft Windows以及包括VM、裸機服務器和雲在內的任何基礎設施上運行。
  3. 安全:Docker賦予應用的隔離性不僅限於彼此隔離,還獨立於底層的基礎設施。Docker默認提供最強的隔離,因此應用出現問題,也只是單個容器的問題,而不會波及到整台機器。

為什麼要用Docker:

  • Docker的鏡像提供了除內核外完整的運行時環境,確保了應用運行環境一致性,從而不會再出現“這段代碼在我機器上沒問題啊”這類問題;——一致的運行環境
  • 可以做到秒級、甚至毫秒級的啟動時間。大大的節約了開發、測試、部署的時間。——更快速的啟動時間
  • 避免公用的服務器,資源會容易受到其他用戶的影響。——隔離性
  • 善於處理集中爆發的服務器使用壓力;——彈性伸縮,快速擴展
  • 可以很輕易的將在一個平台上運行的應用,遷移到另一個平台上,而不用擔心運行環境的變化導致應用無法正常運行的情況。——遷移方便
  • 使用Docker可以通過定製應用鏡像來實現持續集成、持續交付、部署。——持續交付和部署

說起容器,與虛擬機的比較是一個永恆的話題,因為它做的事情和虛擬機做的事情非常的接近。

傳統虛擬機技術是虛擬出一套硬件后,在其上運行一個完整操作系統,在該系統上再運行所需應用進程;而容器內的應用進程直接運行於宿主的內核,容器內沒有自己的內核,而且也沒有進行硬件虛擬。因此容器要比傳統虛擬機更為輕便。

Docker 安裝

Win10 下的安裝

首先介紹一下 win 環境下 Docker 的安裝,打開官網,下載最新版的 Docker Desktop 。

Docker 官方下載地址:

下載前可能需要先註冊下 Docker Hub ,註冊完成後會直接跳轉下載頁面。

下載完成后默認配置 next 就好了。

驗證:

在 CMD 命令行中執行:

docker info

請確保 Docker 正常啟動狀態,否則會報錯的。

小編本地電腦執行后显示如下:

Client:
 Debug Mode: false

Server:
 Containers: 26
  Running: 0
  Paused: 0
  Stopped: 26
 Images: 28
 Server Version: 19.03.1
 ...

內容有些多,我就不全貼出來了,主要會显示一些當前 Docker 相關的信息。

CentOS 下的安裝

懶人神器,使用 CentOS 下的包管理工具 yum 進行安裝,直接輸入:

yum install docker

然後靜靜等待進度條走完,驗證命令和上面一致,同樣需確保 Docker 服務正常啟動。

CentOS 中 Docker 基本操作命令:

# docker 啟動
systemctl start docker
# 重啟 docker服務
systemctl restart docker
# 關閉 docker 服務
systemctl stop docker

驗證結果:

Docker 基礎

首先了解兩個概念:

鏡像:Docker 鏡像是用於創建 Docker 容器的模板。

容器:容器是獨立運行的一個或一組應用。

講人話就是鏡像可以是我們自己的程序,也可以是第三方廠商提供的組件,比如數據庫,緩存服務等等,而這個鏡像想要運行的話,就要放在容器裏面運行,它自己是不能單獨運行的,就好比炒菜,光有菜不行,還要有鍋,菜是在鍋里炒出來的。

因為 Docker 默認的鏡像源是在遙遠的太平洋彼岸,訪問速度會有些慢,建議各位同學配置一個國內的鏡像源。

國內的鏡像源有很多的,百度一下可以找到很多,小編這裏使用的阿里雲提供的容器鏡像加速服務,大家可以登錄自己的淘寶賬號,在阿里雲上找到容器鏡像服務:

具體的配置方案阿里雲已經提供出來了,小編這裏不再贅述,大家自己登錄阿里雲查看吧。

上面這些都配置完成后,我們來講幾個最基礎的 Docker 命令:

獲取鏡像

語法:docker pull NAME[:TAG]
其中,NAME是鏡像倉庫的名稱(用來區分鏡像),TAG是鏡像的標籤(用來表示版本信息)

查看鏡像

語法:docker images

可以看到,小編的機器上目前有一個 mysql5.7 鏡像。

搜索鏡像

語法:docker search [image-name]
從docker倉庫搜索docker鏡像

刪除鏡像

語法:docker rmi NAME
用來刪除指定鏡像,其中後面的參數可以是tag,如果是tag時,實際上是刪除該tag,只要該鏡像還有其他tag,就不會刪除該鏡像。當後面的參數為鏡像ID時,則會徹底刪除整個鏡像,連通所有標籤一同刪除

實戰 hello-world

首先輸入 docker pull hello-world 來拉取 hello-world 鏡像:

拉取成功后,我們使用 docker images 來查看下這個 hello-world 鏡像 :

好了,我們已經看到這個 hello-world 鏡像了,現在來嘗試一下啟動,使用命令 docker run hello-world

若是出現了上圖的內容則說明hello-world運行成功,如果沒出現的話,emmmmmmmmmm,你們可能就要想想自己之前的操作了。

本篇文章到這裏就結束了,希望各位同學能自己動手實踐一下,畢竟實踐出真知嘛。

參考

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

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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 是電子產業重要的元件?

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

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

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

天啦!竟然從來沒有人講過 SpringBoot 支持配置如此平滑的遷移

SpringBoot 是原生支持配置遷移的,但是官方文檔沒有看到這方面描述,在源碼中才看到此模塊,spring-boot-properties-migrator,幸虧我沒有跳過。看到這篇文章的各位,可算是撿到寶了,相信你繼續往下看下去,定會忍不住點贊、收藏、關注。

效果

先放個效果吸引你 🙂

從 SpringBoot 2.0.0 版本開始,配置服務上下文,不支持 server.context-path,而需要server.servlet.context-path配置。但是只要加上以下一個官方依賴,就可以支持使用 server.context-path

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-properties-migrator</artifactId>
    </dependency>

server.context-path 所對應的屬性 ServerProperties#contextPath 在 Java 代碼中已不存在,server.servlet.context-path 所對應的的屬性在內部類 Servlet 中才有,為何加了此依賴就能實現如此神奇的效果呢。

原理

SpringBoot 對外部化配置原生支持遷移功能,所謂遷移,具體是指對應配置的屬性名變動,仍可以使用原來的屬性名配置。
spring-configuration-metadata.json 的信息可以輔助 IDE 進行配置的提示,也可以用來完成配置的遷移。非常的簡單。

相關文章:

通過閱讀代碼,獲得以下信息:

  1. 監聽 ApplicationPreparedEvent 事件(即:環境已準備事件),執行以下操作並收集信息
  2. classpath*:/META-INF/spring-configuration-metadata.json 中載入所有配置
  3. 從上下文的 environment 中過濾出提示的配置(滿足條件:1. deprecation 不為 null,且提示 level 為 error)
  4. 判斷是否兼容(兼容條件見下一節),提取出兼容的屬性
  5. 將 value 對應到 replacement 的 key,並將其屬性源命名為:migrate-原名
  6. 將配置遷移的新屬性源添加到 environment 中,且添加到原屬性源之前(優先級高)。
  7. 監聽事件:ApplicationReadyEvent(應用上下文已準備) 或 ApplicationFailedEvent(應用啟動失敗),打印以上步驟收集的遺留配置信息。以 warn 級別打印兼容的配置,以 error 級別打印不兼容的配置

配置兼容條件

根據元數據中定義的 type 判斷

  1. 如果舊類型、新類型其中之一為 null(元數據中未指定),則不兼容
  2. 如果兩個類型一樣,兼容
  3. 如果新類型是 Duration,而舊類型是 Long 或 Integer,則兼容
  4. 其他情況視為不兼容
  5. environment 中取配置信息,理論上支持 SpringBoot 所有的配置方式

效果

兼容效果:
棄用屬性(如果還存在)與替換后的屬性都會使用配置文件中的棄用的屬性名所對應的的值。

總結

使用配置遷移功能,需要以下步驟:

  1. 引入依賴:spring-boot-properties-migrator(支持配置遷移)、spring-boot-configuration-processor(生成元數據文件,如果已經有完整的,不需要此依賴)
  2. 元數據文件spring-configuration-metadata.json 中棄用屬性名對應的 properties 中必須有 deprecation(在additional-spring-configuration-metadata.json 中添加,相關文章: )
  3. deprecation 中需指定 levelerror
  4. deprecation 中需指定 replacement
  5. replacement 對應的屬性配置在元數據文件中存在,與棄用屬性兼容

經典示例之配置上下文

再說回一開始展示的配置上下文示例。

# 配置 servlet 服務上下文
server:
  context-path: test

從 SpringBoot 2.0.0 版本開始,以上配置不支持,點到配置元數據文件中(spring-configuration-metadata.json),發現如下信息:

{
  "properties": [
    {
      "name": "server.context-path",
      "type": "java.lang.String",
      "description": "Context path of the application.",
      "deprecated": true,
      "deprecation": {
        "level": "error",
        "replacement": "server.servlet.context-path"
      }
    },
    {
      "name": "server.servlet.context-path",
      "type": "java.lang.String",
      "description": "Context path of the application.",
      "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Servlet"
    }

替換屬性名為:server.servlet.context-path,此屬性在org.springframework.boot.autoconfigure.web.ServerProperties 中,且在類中可以發現,server.context-path 所對應的屬性 ServerProperties#contextPath 在代碼中已不存在,而是在內部類 Servlet 中有,也就是對應 server.servlet.context-path 的屬性才有。

但是其滿足配置兼容的條件,為什麼實際上使用卻好像不兼容呢?
其實是因為沒有引入依賴,當引入依賴,就會發現此方式配置可以起作用。

示例之兩種屬性都存在

代碼示例見

1、引入依賴

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

2、Java 配置
此處故意保留棄用屬性

@Data
@Configuration
@ConfigurationProperties(prefix = "my")
public class MyProperties {
  /** the project name */
  private String name;

  private App app;

  @Data
  public static class App {
    private String name;
  }
}

3、元數據配置,spring-configuration-metadata.json 由程序生成,自定義配置放在 additional-spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "my.name",
      "type": "java.lang.String",
      "description": "the project name.",
      "deprecation": {
        "reason": "test the properties-migrator feature.",
        "replacement": "my.app.name",
        "level": "error"
      }
    },
    {
      "name": "my.app.name",
      "type": "java.lang.String",
      "sourceType": "com.lw.properties.migrator.config.MyProperties$App",
      "description": "the project name."
    }
  ]
}

4、在 properties 或 yml 文件中配置

my:
  name: lw
  app:
    name: app

5、打印配置信息

@Slf4j
@SpringBootApplication
public class PropertiesMigratorApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext context =
        SpringApplication.run(PropertiesMigratorApplication.class, args);
    MyProperties myProperties = context.getBean(MyProperties.class);
    log.info("myProperties.name:{}", myProperties.getName());
    log.info(
        "myProperties$app.name:{}",
        Optional.ofNullable(myProperties.getApp()).orElse(new App()).getName());
  }
}

6、打印信息如下:

2019-11-23 21:42:09.580 WARN 109408 — [ main] o.s.b.c.p.m.PropertiesMigrationListener :
The use of configuration keys that have been renamed was found in the environment:

Property source ‘applicationConfig: [classpath:/application.yml]’:
Key: my.name
Line: 4
Replacement: my.app.name
Key: server.context-path
Line: 2
Replacement: server.servlet.context-path

Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.
……… myProperties.name:lw
……… myProperties\(app.name:lw ……… serverProperties\)servlet.contextPath:/app

7、效果解析
在 yml 中棄用屬性名優先級更高,棄用屬性與新屬性都使用此棄用屬性名對應的值。

參考資料

SpringBoot 2.2.1.RELEASE 源碼
公眾號:逸飛兮(專註於 Java 領域知識的深入學習,從源碼到原理,系統有序的學習)

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

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

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

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

嚇人!在浴池洗浴竟會感染梅毒?很多人都不知情…

  本文專家:田靖博士,南部戰區疾病預防控制中心,主治醫師

  我們都知道艾滋病很可怕,得了基本上就需要終生抗爭。

  最近,僅次於艾滋病的第二大性接觸疾病——梅毒,突然成了微博上最熱門的話題。

  某知名企業創始人稱自己“在浴池洗浴感染梅毒,打了 6 天針后治癒”。


微博截圖

  先是洗浴感染梅毒驚呆眾網友,只要在公共中心泡過澡、游過泳的都表示瑟瑟發抖。

  還有對 6 天治癒梅毒表示疑問,畢竟我們所知的梅毒是比較可怕的。

  那泡澡到底會不會感染梅毒?梅毒究竟該如何治癒?我們就網友關心的這些問題科普一下。

  洗浴、游泳真會感染梅毒嗎?

  梅毒螺旋體(Treponema pallidum,TP)又稱為蒼白螺旋體,是造成感染梅毒的“罪魁禍首”。

  目前全球每年約有 1200 萬新發梅毒病例,我國梅毒發病率呈上升趨勢。

  梅毒的感染途徑有——

  1、血液傳播和性接觸傳播

  梅毒螺旋體僅可以感染人類,血液傳播和性接觸傳播是主要途徑。95% 以上的感染者通過危險的無保護的性行為感染,而男同性戀人群中的梅毒感染率近年來急劇上升。

  2、母嬰傳播。梅毒可以通過垂直傳播途徑,由感染梅毒的母親傳染給新生兒。

  梅毒螺旋體病毒感染人類需要具備一定的病毒載量,在某些特殊條件下才能發生,如通過性接觸直接接觸梅毒感染者的創面或者血液傳播,同時也取決於被感染者的身體狀況等。

  因此,常規使用公共設施和出入公共場所,如游泳池游泳、接觸馬桶墊、共用餐具、衣物接觸等情況都不會感染梅毒。

  並且游泳池中的水通常含有漂白粉等消毒劑,不適合淋球菌、梅毒螺旋體等性病病原體存活。所以,去正規的游泳館不存在感染梅毒的可能性。

  梅毒有什麼危害?

  按照《梅毒診斷標準》(WS273-2018),根據梅毒感染的不同階段以及出現癥狀的差異可以將梅毒分為I期、II 期、III 期、胎傳和隱形梅毒。

  I 期~III 期梅毒對於患者的損害是不同的,能造成——

  1. 硬下疳、腹股溝或患部近位淋巴結腫大;
  2. 多個部位的瀰漫性皮損,最終造成頭面部以及四肢產生結節性梅毒疹;
  3. 關節出現結節;
  4. 皮膚、口腔、舌咽出現樹膠腫;
  5. 產生骨梅毒、眼梅毒、心血管梅毒、神經梅毒和其他內臟梅毒等。

  胎傳:所有未經有效治療的梅毒母親所生的嬰兒可能感染胎傳梅毒,根據發病時間分為早期胎傳梅毒、晚期胎傳梅毒和隱性胎傳梅毒。

  隱形梅毒:無臨床癥狀與體征的隱性梅毒患者,仍然具有傳染性,部分病人可以發生晚期損害。

  晚期梅毒可導致不可逆的心血管損傷和中樞系統損害,嚴重者可導致死亡。

  中樞神經系統的梅毒感染可以發生在疾病的任何時期,病程越長、精神癥狀越嚴重。

  此外,梅毒與艾滋病常常是一對“好兄弟”。在感染艾滋病的患者中,大約有 42.8% 的患者都曾感染梅毒。

  梅毒感染會造成皮膚破損,增加體液中 CD4+ 細胞的數量,為 HIV 的感染提供更多的靶細胞,促進 HIV 的傳播,因此從這個意義上來說,感染梅毒是艾滋病的“幫凶”。

  梅毒真能 6 天治癒嗎?

  不一定!

  因為涉及個人體質、治療抗生素的使用方案及青霉素是否過敏等問題,治療周期和結果都會因人而已,最重要是嚴格遵循醫囑和臨床檢查結果。

  因此,治癒的時間根據選擇藥物和患者的病情來確定,不能簡單用時間判斷!

  感染梅毒該如何治療?

  青霉素是治療梅毒的首選藥物之一,對病原菌的細胞壁生成進行抑制 , 從而降低抗原反應素,可短期改善認知功能。青霉素過敏者可採用紅黴素治療。

  多西環素是非青霉素的一種,及早治療血清轉陰率可達 83%-100%,成為當前治療早期梅毒(梅毒螺旋體感染<2 年,II 期早期梅毒)的主要手段。

  頭孢曲松鈉及苄星青霉素聯合治療梅毒比單一用藥效果好。

  不過,抗生素治療僅對免疫系統正常的患者有效。當患者產生梅毒血清抵抗時,單獨使用青霉素,無論增加劑量還是延長治療時間,都對患者無效。

  梅毒治療后,15%~41% 的患者可能形成梅毒血清固定,使用免疫調節劑再治療梅毒血清固定患者有一定的效果,但存在爭議。

  所以,治療梅毒需要嚴格遵循醫囑,才能達到有效治癒。

  此外,還要保持健康良好的生活方式,防止不安全的性行為。

 

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

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

Facebook改進換臉術:無需“投喂”圖片,從視頻里直接變臉

  曉查 發自 凹非寺 
  量子位 報道 公眾號 QbitAI

  近兩年來,Deepfakes 讓許多歐美明星吃盡了苦頭,面對自己的頭像被替換到各種視頻中,卻無能為力。

  比如黑寡婦就對自己的臉被替換到小電影中感到很無奈,呼籲大家停止用 AI 作惡。

  而最近,Facebook 人工智能研究院讓換臉技術再次進化。

  過去 Deepfakes 這項技術需要很多準備材料:一是被替換人臉的原視頻,二是來自換臉人面部各個角度的照片。有這兩樣東西才能造出完美無暇的換臉視頻。

  而來自 Facebook 的技術不需要照片,可以從原視頻直接生成換臉視頻,甚至能對實時視頻進行換臉。

  它讓“大表姐”變得不再熟悉。 

  這項技術的換臉實際上是毫無違和感地修改五官特徵,好讓 AI 無法識別出,因此也就不需要照片了。

  而且 Facebook 的研究人員還表示,這項技術修改后的明星臉仍然可以被人識別出來,但是 AI 卻不行。 

  Facebook 研發這項技術可不是為了換臉好玩,最近因使用人臉識別技術飽受爭議,這家公司希望通過這項新技術來保護用戶的隱私。

  人臉識別和換臉技術對普通民眾的隱私也造成了很大的威脅。比如前一陣大熱的換臉應用 ZAO,讓每個人都享受到換臉帶來的樂趣,但同時也會收集用戶圖片。

  研究人員在論文摘要中說:“人臉識別可能會導致隱私丟失,而換臉技術可能會被用於製作誤導性視頻。”Facebook 用後者來去除視頻中的隱私信息。

  Facebook 聲稱,該技術屬於業內首創,足以抵禦複雜的人臉識別系統。

  Facebook 將在下周韓國首爾舉行的國際計算機視覺國際會議(ICCV)上介紹該工作。

  本周,Facebook 還聯合微軟和亞馬遜,提供 Deepfakes 換臉挑戰數據集,希望能夠提高識別換臉視頻算法的魯棒性,以控制假視頻的傳播。

  此舉頗有些以彼之矛攻彼之盾的意味。

  原文鏈接:

  https://venturebeat.com/2019/10/25/facebook-alters-video-to-make-people-invisible-to-facial-recognition/

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

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

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

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

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

國家電網預計將於2020年6月前全面實現即插即沖新技術

  作者:JoJo

  【TechWeb】10 月 27 日,近日,國網電動汽車服務有限公司發布了充電服務創新模式“車電服務包”。國網電動汽車公司總經理沈建新表示,國網電動汽車公司將加大車聯網的規模,擴大平台充電樁的數量。同時,國網電動汽車公司將在半年內實現“即插即充、無感支付”在公共場站、專用場站、私人充電樁業務場景全覆蓋,加速布局主要城市充電網絡。

  該措施首先是提供主機廠車電包服務範圍內場景全覆蓋、布局更密集的充電設施服務網絡。 國網目前已建成 9 萬自營充電樁,其中高功率直流快充 6.5 萬根。對此,沈建新表示,未來還將引入更多社會資源,擴大充電樁規模,保證主要城市充電站布點半徑不超過 500 米,同一站點充電等候不超過 30 分鐘,確保充電服務套餐用戶實現區域內充電暢行。

  其次,是加快即插即充、無感支付新技術全覆蓋。2020 年 6 月前,通過技術升級及硬件改造,國網電動汽車公司將完成國網系統充電樁全面支持即插即充,新投入車聯網平台的充電樁全部滿足“即插即充、無感支付”要求,實現“充綠色電,比加油更方便”。

  此次發布的“車電服務包”是與四家車企聯合推出與新車綁定銷售的充電產品,用戶一次付費購買“車電服務包”並綁定車輛后,即可在國網充電樁上享受“即插即充、無感支付”。電動汽車插入充電槍后,充電過程不需要人為干預,自動完成認證、充電啟動、充電停止以及訂單生成與結算,實現了車、樁、網、能源的泛在互聯與高效互動,是國家電網公司泛在電力物聯網建設的典型終端。

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

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

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

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

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