面試題

怎么保證異步操作 在事務提交之后執(zhí)行

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            System.out.println("send email after transaction commit...");
        }
    });

TransactionSynchronizatonManger.registerSynchorinization()的方法营罢,傳入一個TransactionSynchornizationAdapter抽象類的實現,實現他的 afterCommit鉤子方法淀弹。
里面還有beforeCommit()方法

怎么保證多數據源 事務一致性

  • 基于XA協(xié)議的兩階段提交方案
    開源框架 atomikos
    常用的商用數據庫都支持XA協(xié)議,有個全局協(xié)調者的概念,
    第一階段是表決階段亏娜,所有參與者都將本地事務是否能成功的信息反饋給協(xié)調者,
    第二階段是執(zhí)行階段镇匀,協(xié)調者根據所有參與者的反饋照藻,通知所有參與者 你們要提交還是回滾。


    XA

    缺點:兩階段提交方案鎖定資源時間長汗侵,對性能影響很大幸缕,基本不適合解決微服務事務問題。

  • TCC方案
    TCC方案在電商晰韵、金融領域落地較多发乔。TCC方案其實是兩階段提交的一種改進。其將整個業(yè)務邏輯的每個分支顯式的分成了Try雪猪、Confirm栏尚、Cancel三個操作。Try部分完成業(yè)務的準備工作只恨,confirm部分完成業(yè)務的提交译仗,cancel部分完成事務的回滾」倜伲基本原理如下圖所示纵菌。
    tcc方案

事務開始時,業(yè)務應用會向事務協(xié)調器注冊啟動事務休涤。之后業(yè)務應用會調用所有服務的try接口咱圆,完成一階段準備。之后事務協(xié)調器會根據try接口返回情況功氨,決定調用confirm接口或者cancel接口序苏。如果接口調用失敗,會進行重試捷凄。

TCC方案讓應用自己定義數據庫操作的粒度忱详,使得降低鎖沖突、提高吞吐量成為可能纵势。 當然TCC方案也有不足之處踱阿,集中表現在以下兩個方面:

1.對應用的侵入性強。業(yè)務邏輯的每個分支都需要實現try钦铁、confirm、cancel三個操作才漆,應用侵入性較強牛曹,改造成本高。
2.實現難度較大醇滥。需要按照網絡狀態(tài)黎比、系統(tǒng)故障等不同的失敗原因實現不同的回滾策略超营。為了滿足一致性的要求,confirm和cancel接口必須實現冪等阅虫。

上述原因導致TCC方案大多被研發(fā)實力較強演闭、有迫切需求的大公司所采用

\color{red}{微服務倡導服務的輕量化、易部署颓帝,而TCC方案中很多事務的處理邏輯需要應用自己編碼實現米碰,復雜且開發(fā)量大。}

重點來了9撼恰B雷!

  • 基于消息的最終一致性方案
    消息一致性方案是通過消息中間件保證上瘪板、下游應用數據操作的一致性吴趴。基本思路是將本地操作和發(fā)送消息放在一個事務中侮攀,保證本地操作和消息發(fā)送要么兩者都成功或者都失敗锣枝。下游應用向消息系統(tǒng)訂閱該消息,收到消息后執(zhí)行相應操作兰英。
    基于中間件實現的最終一致性方法

消息方案從本質上講是將分布式事務轉換為兩個本地事務撇叁,然后依靠下游業(yè)務的重試機制達到最終一致性〖牵基于消息的最終一致性方案對應用侵入性也很高税朴,應用需要進行大量業(yè)務改造,成本較高家制。

事務的隔離級別正林、傳播機制

ACID
原子性:事務是一個不可分割的執(zhí)行單元,事務中的所有操作要么全都執(zhí)行颤殴,要么全都不執(zhí)行觅廓。
隔離性:一致性要求,事務在開始前和結束后涵但,數據庫的完整性約束沒有被破壞杈绸。
一致性:事務的執(zhí)行是相互獨立的,它們不會相互干擾矮瘟,一個事務不會看到另一個正在運行過程中的事務的數據瞳脓。
持久性:持久性要求,一個事務完成之后澈侠,事務的執(zhí)行結果必須是持久化保存的劫侧。即使數據庫發(fā)生崩潰,在數據庫恢復后事務提交的結果仍然不會丟失。

談到隔離級別烧栋,我們不妨先聊一下 事務并發(fā)的時候可能會出現的問題

當數據庫上有多個事務同時執(zhí)行的時候写妥,就可能出現臟讀(dirty read)、不可重復讀(non-repeatable read)审姓、幻讀(phantom read)的問題珍特,為了解決這些問題,就有了“隔離級別”的概念魔吐。

  • 臟讀: 事務A讀取了事務B更新的數據扎筒,然后B回滾操作,那么A讀取到的數據是臟數據
  • 不可重復度: 事務 A 多次讀取同一數據画畅,事務 B 在事務A多次讀取的過程中砸琅,對數據作了更新并提交,導致事務A多次讀取同一數據時轴踱,結果 不一致症脂。
  • 幻讀:系統(tǒng)管理員A將數據庫中所有學生的成績從具體分數改為ABCDE等級,但是系統(tǒng)管理員B就在這個時候插入了一條具體分數的記錄淫僻,當系統(tǒng)管理員A改結束后發(fā)現還有一條記錄沒有改過來诱篷,就好像發(fā)生了幻覺一樣,這就叫幻讀雳灵。

不可重復讀的和幻讀很容易混淆棕所,不可重復讀側重于修改,幻讀側重于新增或刪除悯辙。解決不可重復讀的問題只需鎖住滿足條件的行琳省,解決幻讀需要鎖表

image.png

mysql的默認隔離級別是 可重復讀

  • 讀未提交是指,一個事務還沒提交時躲撰,它做的變更就能被別的事務看到针贬。
  • 讀提交是指,一個事務提交之后拢蛋,它做的變更才會被其他事務看到桦他。
  • 可重復讀是指,一個事務執(zhí)行過程中看到的數據谆棱,總是跟這個事務在啟動時看到的數據是一致的快压。當然在可重復讀隔離級別下,未提交變更對其他事務也是不可見的垃瞧。
  • 串行化蔫劣,顧名思義是對于同一行記錄,“寫”會加“寫鎖”个从,“讀”會加“讀鎖”拦宣。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執(zhí)行完成信姓,才能繼續(xù)執(zhí)行鸵隧。


    示例

我們來看看在不同的隔離級別下,事務A會有哪些不同的返回結果意推,也就是圖里面V1豆瘫、V2、V3的返回值分別是什么菊值。

  • 若隔離級別是“讀未提交”外驱, V1 = 2, v2= 2,V3 = 2
  • 若隔離級別是“讀提交”, V1 = 1,V2=2,V3 =2
  • 若隔離級別是“可重復讀腻窒, V1 = 1 ,V2 = 1,V3 =2
  • 若隔離級別是“串行化”昵宇, V1 = 2,V2 = 1,V3=2

隔離得越嚴實,效率就會越低儿子。因此很多時候瓦哎,我們都要在二者之間尋找一個平衡點。

線程池七大參數含義

  • corePollSize 核心線程數具篇。在創(chuàng)建了線程池后吃挑,線程中沒有任何線程号胚,等到有任務到來時才創(chuàng)建線程去執(zhí)行任務。默認情況下犯助,在創(chuàng)建了線程池后,線程池中的線程數為0维咸,當有任務來之后剂买,就會創(chuàng)建一個線程去執(zhí)行任務,當線程池中的線程數目達到corePoolSize后癌蓖,就會把到達的任務放到緩存隊列當中瞬哼。
  • maximumPoolSize 最大線程數 表明線程中最多能夠創(chuàng)建的線程數量。
  • keepAliveTime 空閑的線程保留的時間费坊。
  • TimeUnit 保留的時間單位
  • BlockingQueue<Runnable> 阻塞隊列倒槐,存儲等待執(zhí)行的任務,ArrayBlockingQueue附井、LinkedBlockingQueue讨越、SynchronousQueue可選
  • ThreadFactory:線程工廠,用來創(chuàng)建線程
  • RejectedExecutionHandler 拒絕策略

ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常永毅。 
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務把跨,但是不拋出異常。 
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務沼死,然后重新嘗試執(zhí)行任務(重復此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

*newFixedThreadPool 定長線程池着逐,可控制線程的最大并發(fā)數,超出的線程會在隊列中等待。
*newCachedThreadPool 創(chuàng)建一個可緩存線程池耸别,如果線程池長度超過處理需要健芭,可靈活回收空閑線程,若無可回收秀姐,則新建線程慈迈。
如果沒有現有的線程可用,那么就創(chuàng)建新的線程并添加到池中省有,線程沒有使用60秒的時間被終止并從線程池里移除緩存痒留。
*newScheduledThreadPool (四該球的)創(chuàng)建一個定長任務線程池,支持定時及周期性任務執(zhí)行蠢沿。

  • newSingleThreadExecutor 創(chuàng)建一個單線程的線程池伸头,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行舷蟀。


    image.png

索引失效的場景

  • 索引列上使用內置函數
  • 小表查詢恤磷,數據量小
  • 隱式轉換導致索引失效 吧字符類型當做數字傳過來
  • 對索引列進行運算導致索引失效,我所指的對索引列進行運算包括(+,-雪侥,*碗殷,/,! 等)
  • %開頭
  • NOT IN和<>操作
    NOT IN可以NOT EXISTS代替速缨,id<>3則可使用id>3 or id<3來代替锌妻。
  • 索引不會包含有NULL值的列
    只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值旬牲,那么這一列對于此復合索引就是無效的仿粹。所以我們在數據庫設計時不要讓字段的默認值為NULL。

哪些字段應該建立索引

  • 主鍵自動建立唯一索引原茅;
  • 頻繁作為查詢條件的字段應該創(chuàng)建索引
  • 查詢中與其它表關聯(lián)的字段吭历,外鍵關系建立索引(當然現在很多企業(yè)都是不讓用外鍵的)
  • 單鍵/組合索引的選擇問題, 組合索引性價比更高
  • 查詢中排序的字段擂橘,排序字段若通過索引去訪問將大大提高排序速度
  • 查詢中統(tǒng)計或者分組字段

哪些字段不應該建立索引

  • 表記錄太少
  • 經常增刪改的表或者字段
  • where條件里用不著的不經常用的
  • 過濾性(離散型)不好的晌区,例如性別

sql執(zhí)行計劃 參數含義

可以告訴我們,sql如何使用索引通贞,查詢的執(zhí)行順序朗若,掃描的數據行數


explain
  • id 包含一組數字,表示執(zhí)行select字句或操作表的順序
  • select_type: 代表查詢的類型昌罩,主要用來區(qū)分普通查詢哭懈、聯(lián)合查詢、子查詢和復雜查詢茎用,例如SIMPLE 簡單查詢遣总、PRIMARY睬罗,如果有子查詢,最外層查詢標記為PRIMARY
  • table 哪張表
  • type 查詢的訪問類型旭斥、是比較重要的一個指標容达,保證最少 是 range級別,最好是ref琉预,最壞是ALL全表掃
  • key 實際使用的索引董饰,如果為NULL,不用索引
  • key_len 表示索引中使用的字節(jié)數。 key_len越長說明索引利用的越充分圆米,


    image.png
  • rows 執(zhí)行查詢時必須要檢查的行數,越少越好

常用的設計模式以及描述

redis memche mongdb 比較

JUC里的核心類

  • ReentrantReadWriteLock 讀寫鎖
  • CountDownLatch 線程計數器啄栓,發(fā)令槍娄帖,
    CountDownLatch 主要有兩個方法,當一個或多個線程調用 await 方法時昙楚,這些線程會阻塞近速。
    其它線程調用 countDown 方法會將計數器減 1(調用 countDown 方法的線程不會阻塞),當計數器的值變?yōu)?0 時堪旧, 因 await 方法阻塞的線程會被喚醒削葱,繼續(xù)執(zhí)行。
  • CyclicBarrier 循環(huán)柵欄
image.png
image.png
  • image.png
image.png

AtomicInteger 以及相關原子類

Unsafe + CAS
更新的時候淳梦,傳入預期值析砸,和內存偏移量 根據內存拿到實際值,跟預期值不符就繼續(xù)循環(huán)遍歷 知道更新成功為止

舉例說明: 就像我們git提交代碼一樣(提交之前不pull爆袍,本地版本跟遠程倉庫版本一致方可提交)首繁,我們總是樂觀的認為在我之前沒人改過,提交的時候發(fā)現 別人已經搶先提交陨囊,那我只能重新pull一下保持跟遠程康庫一致然后繼續(xù)嘗試提交弦疮,又倒霉的發(fā)現 有人搶先提交了,那我只能再pull一次 保持跟倉庫一致然后繼續(xù)提交蜘醋。 提高CAS無鎖算法的 效率 可以從減少循環(huán)次數 下文章胁塞,例如,我先等一會压语,再pull然后提交啸罢。

Synchroniz和讀寫鎖原理

springboot的run方法里都干了啥、

image.png

public ConfigurableApplicationContext run(String... args) {
        //開啟計時
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        //初始化監(jiān)聽器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //發(fā)布ApplicationStartingEvent
        listeners.starting();
 
        try {
            //裝配參數和環(huán)境
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //發(fā)布ApplicationEnvironmentPreparedEvent
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            //創(chuàng)建ApplicationContext,并裝配
            context = this.createApplicationContext();
            this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //發(fā)布ApplicationPreparedEvent
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            
            //發(fā)布ApplicationStartedEvent
            listeners.started(context);
            //執(zhí)行Spring中@Bean下的一些操作无蜂,如靜態(tài)方法等
            this.callRunners(context, applicationArguments);
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, exceptionReporters, var9);
            throw new IllegalStateException(var9);
        }
 
        //發(fā)布ApplicationReadyEvent
        listeners.running(context);
        return context;

入口 SpringApplication.run(BeautyApplication.class, args);
首先記錄整個Spring Application的加載時間伺糠!

1.初始化監(jiān)聽器
2.發(fā)布ApplicationStartingEvent
3.發(fā)布ApplicationEnvironmentPreparedEvent
4.打印banner
5.創(chuàng)建ApplicationContext,并裝配
6.發(fā)布ApplicationPreparedEvent
7.refreshContext(context); //IOC容器加載過程 這里面的onRefresh() 創(chuàng)建了web容器
8.refreshContext(context);

  1. this.afterRefresh(context, applicationArguments);

springboot核心注解

@SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

這玩意是個組合注解,主要包含以下幾個

  • @SpringBootConfiguration 里面又包含了 @Configuration斥季,可以讓我們額外注冊一些Bean训桶,并導入一些額外的配置累驮。 把一個類變成一個配置類,不需要額外的XML進行配置舵揭。
  • @EnableAutoConfiguration 官網上說讓Spring去進行一些自動配置谤专,由@AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class),兩個組合而成。
    @AutoConfigurationPackage: 讓包中的類以及子包中的類能夠被自動掃描到spring容器中午绳。
    @Import(EnableAutoConfigurationImportSelector.class):這個是核心置侍,我們說老自動配置,那他到底幫我們配置了什么拦焚,怎么配置的蜡坊?
    EnableAutoConfigurationImportSelector這個類的作用就是 去META-INF/spring.factories目錄下下的類并加入的程序中,
    image.png

    我們可以發(fā)現幫我們配置了很多類的全路徑赎败,比如你想整合activemq秕衙,或者說Servlet
    并不是所有的類都給你創(chuàng)建好,而是根據程序進行決定
  • @ComponentScan: 掃描包僵刮,放入spring容器据忘,那他在springboot當中做了什么策略呢?
    他幫我們做了一個策略搞糕,他在這里結合SpringBootConfiguration去使用勇吊,為什么是排除,因為不可能一上來全部加載窍仰,因為內存有限汉规。
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

那么我們來總結下@SpringbootApplication:就是說,他已經把很多東西準備好辈赋,具體是否使用取決于我們的程序或者說配置鲫忍,那我們到底用不用?那我們繼續(xù)來看一行代碼

public static void main(String[] args)
{
    SpringApplication.run(StartEurekaApplication.class, args);
}

那們來看下在執(zhí)行run方法到底有沒有用到哪些自動配置的東西钥屈,比如說內置的Tomcat悟民,那我們來找找內置Tomcat,我們點進run

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return new SpringApplication(sources).run(args);
    }

然后他調用又一個run方法篷就,我們點進來看

public ConfigurableApplicationContext run(String... args) {
   //計時器
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
   //監(jiān)聽器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      Banner printedBanner = printBanner(environment);
      //準備上下文
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
         //預刷新context
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
     //刷新context
      refreshContext(context);
     //刷新之后的context
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

那我們關注的就是 refreshContext(context); 刷新context射亏,我們點進來看

private void refreshContext(ConfigurableApplicationContext context) {
   refresh(context);
   if (this.registerShutdownHook) {
      try {
         context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
         // Not allowed in some environments.
      }
   }
}

我們繼續(xù)點進refresh(context);

protected void refresh(ApplicationContext applicationContext) {
   Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
   ((AbstractApplicationContext) applicationContext).refresh();
}

他會調用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點進來看

// 完成IoC容器的創(chuàng)建及初始化工作
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      //   1: 刷新前的準備工作竭业。
      prepareRefresh();

       // 告訴子類刷新內部bean 工廠智润。
      //  2:創(chuàng)建IoC容器(DefaultListableBeanFactory),加載解析XML文件(最終存儲到Document對象中)
      // 讀取Document對象,并完成BeanDefinition的加載和注冊工作
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      //  3: 對IoC容器進行一些預處理(設置一些公共屬性)
      prepareBeanFactory(beanFactory);

      try {
         //  4:  允許在上下文子類中對bean工廠進行后處理未辆。
         postProcessBeanFactory(beanFactory);

         //  5: 調用BeanFactoryPostProcessor后置處理器對BeanDefinition處理
         invokeBeanFactoryPostProcessors(beanFactory);

        //  6: 注冊BeanPostProcessor后置處理器
         registerBeanPostProcessors(beanFactory);

         //  7: 初始化一些消息源(比如處理國際化的i18n等消息源)
         initMessageSource();

          //  8: 初始化應用事件多播器
         initApplicationEventMulticaster();

        //  9: 初始化一些特殊的bean   例如 tomcat  容器窟绷。。咐柜。
         onRefresh();

         // 10: 注冊一些監(jiān)聽器      用戶自己寫的監(jiān)聽器
         registerListeners();

         //  11: 實例化剩余的單例bean(非懶加載方式)
      //      注意事項:Bean的IoC兼蜈、DI和AOP都是發(fā)生在此步驟
         finishBeanFactoryInitialization(beanFactory);

         //12: 完成刷新時攘残,需要發(fā)布對應的事件
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         //  銷毀已經創(chuàng)建的單例避免占用資源
         destroyBeans();

         //  重置'active' 標簽。
         cancelRefresh(ex);

         //傳播異常給調用者
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

這點代碼似曾相識啊 沒錯为狸,就是一個spring的bean的加載過程我在歼郭,解析springIOC加載過程的時候介紹過這里面的方法,如果你看過Spring源碼的話 辐棒,應該知道這些方法都是做什么的〔≡現在我們不關心其他的,我們來看一個方法叫做 onRefresh()方法

protected void onRefresh() throws BeansException {
   // For subclasses: do nothing by default.
}
image.png

我們既然要找Tomcat那就肯定跟web有關漾根,我們可以看到有個ServletWebServerApplicationContext

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

我們可以看到有一個createWebServer();方法他是創(chuàng)建web容器的泰涂,而Tomcat不就是web容器,那他是怎么創(chuàng)建的呢立叛,我們繼續(xù)看

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
   if (webServer == null && servletContext == null) {
      ServletWebServerFactory factory = getWebServerFactory();
      this.webServer = factory.getWebServer(getSelfInitializer());
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context",
               ex);
      }
   }
   initPropertySources();
}

factory.getWebServer(getSelfInitializer());他是通過工廠的方式創(chuàng)建的

public interface ServletWebServerFactory {

   WebServer getWebServer(ServletContextInitializer... initializers);

}

可以看到 它是一個接口负敏,為什么會是接口。因為我們不止是Tomcat一種web容器秘蛇。


image.png

我們看到還有Jetty,那我們來看TomcatServletWebServerFactory

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null) ? this.baseDirectory
         : createTempDir("tomcat");
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
   return getTomcatWebServer(tomcat);
}

那這塊代碼顶考,就是我們要尋找的內置Tomcat赁还,在這個過程當中,我們可以看到創(chuàng)建Tomcat的一個流程驹沿。因為run方法里面加載的東西很多艘策,所以今天就淺談到這里。如果不明白的話渊季, 我們在用另一種方式來理解下朋蔫,

https://github.com/zgw1469039806/gwspringbootsrater

spring核心類

zk場景

*dubbo的服務注冊發(fā)布(自動),公司自研的rpc服務管理平臺(手動)

  • 配置中心 改配置不需要機器 上線却汉,相關程序對配置中心 進行節(jié)點監(jiān)控驯妄,一旦配置中心數據發(fā)生變化,應用就會收到zk的通知
  • 分布式鎖

zk通知機制 選舉機制

  • 通知機制
    ZooKeeper 支持 watch(觀察)的概念合砂,客戶端可以在每個 znode 結點上設置一個觀察青扔。如果被觀察服務端的 znode結點有變更,那么 watch 就會被觸發(fā)翩伪,這個 watch 所屬的客戶端將接收到一個通知包被告知結點已經發(fā)生變化微猖,把 相應的事件通知給設置過 Watcher 的 Client 端。
    異步回調的觸發(fā)機制

一次觸發(fā)缘屹,觸發(fā)一次就失效凛剥,想繼續(xù)監(jiān)聽,需要客戶端重新設置 Watcher轻姿。因此如果你得到一個 watch 事件且想在 將來的變化得到通知犁珠,必須新設置另一個 watch逻炊。

JVM運行時數據區(qū)

image.png
  • 程序計數器 指向當前線程所執(zhí)行的字節(jié)碼的行號,線程切換還能知道在哪個位置繼續(xù)執(zhí)行盲憎,分支嗅骄,循環(huán),跳轉饼疙,異常處理都通過它完成

  • 虛擬機棧 每個方法的執(zhí)行都對應這虛擬機棧的一個棧幀的入棧 出棧操作溺森,存儲存儲局部變量表,操作數棧窑眯,動態(tài)鏈接屏积,方法出口等信息

  • 本地方法棧 本地方法棧和虛擬機棧相同,里面的方法都是 native方法
    *堆
    堆是JVM里最大的一塊內存區(qū)域磅甩,被所有線程共享炊林,在虛擬機啟動時創(chuàng)建,是垃圾收集的主要區(qū)域
    存放對象實例卷要,數組渣聚,

  • 方法區(qū) 也是線程共享的, 存儲已被虛擬機加載的類信息僧叉,常量(final)奕枝,靜態(tài)變量(static)
    運行時常量池是方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號引用瓶堕,

雙親委派隘道,沙箱安全

如果一個類加載器收到了類加載請求,它并不會自己先去加載郎笆,而是把這個請求委托給父類的加載器去執(zhí)行谭梗,如果父類加載器還存在其父類加載器,則進一步向上委托宛蚓,依次遞歸激捏,請求最終將到達頂層的啟動類加載器,如果父類加載器可以完成類加載任務苍息,就成功返回缩幸,倘若父類加載器無法完成此加載任務,子加載器才會嘗試自己去加載竞思,這就是雙親委派模式表谊,即每個兒子都不愿意干活,每次有活就丟給父親去干盖喷,直到父親說這件事我也干不了時爆办,兒子自己想辦法去完成
> 1.避免重復加載類 2.考慮安全因素,java核心api中定義類型不會被隨意替換课梳,假設通過網絡傳遞一個名為java.lang.Integer的類距辆。余佃。。防止核心API被篡改

類加載

加載 – 連接 – 初始化 – 使用 – 卸載
源代碼(.java文件)經過編譯后會變成字節(jié)碼(.class文件)
類加載跨算,指的是將類的.class文件中的二進制數據讀入到內存中爆土,把它放進運行時數據區(qū)的方法區(qū)內(Perm區(qū))。
然后在堆區(qū)創(chuàng)建一個java.lang.Class對象诸蚕,封裝這個類在自身的方法區(qū)內的數據結構步势。
注意,這是仍舊沒有生成針對該類的對象背犯。后續(xù)對類的實例化坏瘩,會使用堆內存中的Class對象生成具體的實例對象。

確定垃圾

  • 引用計數法
    給對象中添加一個引用計數器漠魏,每當有一個地方引用它時倔矾,計數器值就加1;當引用失效時柱锹,計數器值就減1哪自;任何時刻計數器為0的對象就是不可能再被使用的。該方法實現簡單禁熏,效率高提陶,但是它很難它很難解決對象之間相互循環(huán)引用的問題。
  • GCRoot可達性分析
    這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的匹层。


    GC ROOT

哪些對象可以視為GC ROOT

  • 1 虛擬機棧中引用的對象
  • 2 方法區(qū)中靜態(tài)屬性、常量引用的對象
  • 3 Native方法引用的對象

GC過程

  • Minor GC
    大多數情況锌蓄,對象出生在Eden升筏,當Eden區(qū)沒有足夠空間的時候,觸發(fā)一次Minor GC
    新生代GC瘸爽,大多數java對象朝生夕滅您访,Minor GC 比較頻繁,速度也比較快
  • Full GC

對象何時進入老年代

  • 新建對象 無法在新生代的幸存區(qū)存儲
  • 垃圾回收超過15次 Eden中的對象經歷一次minor GC 剪决,如果還存活的話灵汪,進入s1,在s1中熬過一次 minor GC柑潦,進入s2
  • 大對象

調優(yōu)參數

調優(yōu)工具

image.png

線上問題排查

1.通過top命令查看當前機器的CPU使用情況,看看是哪個進程CPU高

  1. ps-ef或者jps 進一步定位享言,得知是怎么樣一個后臺程序惹事了
  2. 定位到具體的線程 ps-mp 進程id -o THREAD,tid,time
  3. 將有問題IDE線程ID轉換為16進制格式 printf "%x/n" 有問題的線程ID
  4. jstack 進程ID | grep 16進制線程id 查看線程堆棧,看看帶公司代碼的那幾行

為什么產生死鎖渗鬼,如何避免览露?

是指多個線程在運行過程中因爭奪資源而造成的一種僵局。

  • 原因
    舉例: 線程1按照先獲得鎖A,再去獲得鎖B的順序執(zhí)行譬胎,在此同時又一有一個鎖線程2差牛,按照先獲取鎖B再獲得鎖A的順序執(zhí)行命锄,線程1獲得了鎖A,此時線程2獲得了鎖B,他們都在互相等待對方釋放鎖,因此兩人都卡在那偏化,造成死鎖脐恩。
  • 預防
    1.以確定的順序獲得鎖
    首先我們要盡量避免多個線程去競爭多個鎖的情況,如果避免不了侦讨,在設計的時候要充分 考慮不同線程之間獲取鎖的順序驶冒。
  1. 超時放棄
    當使用synchronized關鍵詞提供的內置鎖時,只要線程沒有獲得鎖搭伤,那么就會永遠等待下去只怎,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,該方法可以按照固定時長等待鎖怜俐,因此線程可以在獲取鎖超時以后身堡,主動釋放之前已經獲得的所有的鎖。通過這種方式拍鲤,也可以很有效地避免死鎖贴谎。
  • 死鎖檢測
    1.Jstack命令
    打印出給定的java進程ID的Java堆棧信息, Jstack工具可以用于生成java虛擬機當前時刻的線程快照季稳。擅这。線程快照是當前java虛擬機內每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因景鼠,如線程間死鎖仲翎、死循環(huán)、請求外部資源導致的長時間等待等铛漓。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧溯香,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源浓恶。

2.JConsole工具
Jconsole是JDK自帶的監(jiān)控工具玫坛,在JDK/bin目錄下可以找到。它用于連接正在運行的本地或者遠程的JVM包晰,對運行在Java應用程序的資源消耗和性能進行監(jiān)控湿镀,并畫出大量的圖表,提供強大的可視化界面伐憾。


類加載

是java程序"一次編譯,到處運行"的關鍵,java程序編譯時,不是直接編譯成目標機器的機器碼,而是編譯成.class的二級制的字節(jié)碼文件,再由目標機器上的JVM虛擬機把.class文件翻譯為對應機器的機器碼執(zhí)行.

"加載"就是將.class文件讀到內存中

1.加載2.驗證3.準備4.解析5.初始化 6.使用
驗證 準備 解析 又成為連接
1.加載2.連接勉痴。3初始化

  • 加載
    加載主要是將.class文件讀取到內存中,主要做三件事:1.通過類的全限定名獲取該類的二進制字節(jié)流塞耕; 2.將字節(jié)流所代表的靜態(tài)存儲結構轉化為方法區(qū)的運行時數據結構蚀腿;3.在內存中生成一個該類的java.lang.Class對象,作為方法區(qū)這個類的各種數據的訪問入口狸页。

  • 驗證
    驗證是連接階段的第一步悼院,主要確保加載進來的字節(jié)流符合JVM規(guī)范瀑焦。
    1.文件格式驗證
    2.元數據驗證(是否符合Java語言規(guī)范)
    3.字節(jié)碼驗證(確定程序語義合法托猩,符合邏輯)
    4.符號引用驗證(確保下一步的解析能正常執(zhí)行)

  • 準備
    主要為靜態(tài)變量在方法區(qū)分配內存含懊,并設置默認初始值驰唬。

  • 解析
    解析是連接階段的第三步比被,是虛擬機將常量池內的符號引用替換為直接引用的過程可婶。

  • 初始化
    類加載過程中最后一步蚊伞,主要是根據程序中的賦值語句主動為類變量賦值席赂。
    注:
    1.當有父類且父類為初始化的時候,先初始化父類
    2.再進行初始化語句
什么時候需要對類進行初始化时迫?

1.使用new該類實例化對象的時候颅停;
2.讀取或設置類靜態(tài)字段的時候(但被final修飾的字段,在編譯器時就被放入常量池的靜態(tài)字段除外static final)掠拳;
3.調用類靜態(tài)方法的時候
4.使用反射Class.forName(“xxxx”)對類進行反射調用的時候癞揉,該類需要初始化;
5.初始化一個類的時候溺欧,有父類喊熟,先初始化父類(注:1. 接口除外,父接口在調用的時候才會被初始化姐刁;2.子類引用父類靜態(tài)字段芥牌,只會引發(fā)父類初始化);
6.被標明為啟動類的類(即包含main()方法的類)要初始化聂使;


java反射獲取私有屬性壁拉,改變值

Field age = clazz.getDeclaredField("age");
age.setAccessible(true);
age.set(wdc,66);
或者獲取所有的屬性,去遍歷


數據庫樂觀鎖使用

增加一個數字類型的 “version”柏靶,當讀取數據時扇商,將version字段的值一同讀出,數據每更新一次宿礁,對此version值加一。當我們提交更新的時候蔬芥,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對梆靖,如果數據庫表當前版本號與第一次取出來的version值相等,則予以更新笔诵,否則認為是過期數據返吻。

update task set value = newValue, version = versionValue + 1 where version = versionValue;

如何開啟慢查詢日志

slow_query_log 設置為ON
設置滿查詢日志的存放位置
set global slow_query_log_file='/var/lib/mysql/test-10-226-slow.log';
設置超過幾秒就記錄
set global long_query_time=1;

如何設計一個線程安全的HashMap

1.將所有public方法都加上synchronized: 相當于設置了一把全局鎖,所有操作都需要先獲取鎖(比如直接使用Collections.synchronizedMap()方法對其進行加鎖操作)乎婿,java.util.HashTable就是這么做的测僵,性能很低

  1. 由于每個桶在邏輯上是相互獨立的,將每個桶都加一把鎖,如果兩個線程各自訪問不同的桶捍靠,就不需要爭搶同一把鎖了沐旨。這個方案的并發(fā)性比單個全局鎖的性能要好,不過鎖的個數太多榨婆,也有很大的開銷磁携。
    3.鎖分離(Lock Stripping)技術:第2個方法把鎖的壓力分散到了多個桶,理論上是可行的的良风,但是假設有1萬個桶谊迄,就要新建1萬個ReentrantLock實例,開銷很大烟央⊥撑担可以將所有的桶均勻的劃分為16個部分,每一部分成為一個段(Segment)疑俭,每個段上有一把鎖粮呢,這樣鎖的數量就降低到了16個,JDK 7里的java.util.concurrent.ConcurrentHashMap就是這個思路怠硼。
    4.在JDK8里鬼贱,ConcurrentHashMap的實現又有了很大變化,它在鎖分離的基礎上香璃,大量利用了了CAS指令这难。并且底層存儲有一個小優(yōu)化,當鏈表長度太長(默認超過8)時葡秒,鏈表就轉換為紅黑樹姻乓。鏈表太長時,增刪查改的效率比較低眯牧,改為紅黑樹可以提高性能

快排基本思想

1.先從數列中取出一個數作為基準數
2.分區(qū)過程蹋岩,將比這個數大的數全放到它的右邊,小于或等于它的數全放到它的左邊
3.再對左右區(qū)間重復第二步学少,直到各區(qū)間只有一個數

垃圾回收機制


項目中查看垃圾回收


synchronized 修飾靜態(tài) 方法和普通方法的區(qū)別剪个,獲取類鎖之后還能獲取對象鎖嗎?

synchronized修飾不加static的方法版确,鎖是加在單個對象上扣囊,不同的對象沒有競爭關系
修飾加了static的方法,鎖是加載類上绒疗,這個類所有的對象競爭一把鎖侵歇。


如何將數據分布在不同的redis

redis3.0之后哈西槽
一致性哈希算法


springAOP的實現


瀏覽器輸入網址全過程,結合springmvc


數據庫的默認隔離級別吓蘑,一定會產生幻讀嗎惕虑?怎么解決?


負載均衡算法


SpringBean的默認范圍


如何確保多臺機器不重復消費


paxos算法


springMVC流程

image.png

image.png

第一步:發(fā)起請求到前端控制器(DispatcherServlet)

第二步:前端控制器請求HandlerMapping查找 Handler (可以根據xml配置、注解進行查找)

第三步:處理器映射器HandlerMapping向前端控制器返回Handler溃蔫,HandlerMapping會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象健提,多個HandlerInterceptor攔截器對象),通過這種策略模式酒唉,很容易添加新的映射策略

第四步:前端控制器調用處理器適配器去執(zhí)行Handler

第五步:處理器適配器HandlerAdapter將會根據適配的結果去執(zhí)行Handler

第六步:Handler執(zhí)行完成給適配器返回ModelAndView

第七步:處理器適配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一個底層對象矩桂,包括 Model和view)

第八步:前端控制器請求視圖解析器去進行視圖解析 (根據邏輯視圖名解析成真正的視圖(jsp)),通過這種策略很容易更換其他視圖技術痪伦,只需要更改視圖解析器即可

第九步:視圖解析器向前端控制器返回View

第十步:前端控制器進行視圖渲染 (視圖渲染將模型數據(在ModelAndView對象中)填充到request域)

第十一步:前端控制器向用戶響應結果


左連接右連接的區(qū)別

左連接是返回主表的所有信息侄榴,從表只返回滿足條件的
右連接 是返回主表滿足條件的信息,從表全部返回
內連接 只滿足返回條件的


JVM有哪些垃圾回收器网沾,如何查看默認垃圾回收器,怎么配置垃圾回收器

如果說垃圾收集算法是內存回收的方法論癞蚕,那么垃圾收集器那么垃圾收集器就是內存回收的具體實現。
GC回收算法是內存回收的方法論辉哥,總要有落地的實現桦山,
4種主要的垃圾收集器:Serial(塞銳嘔)串行回收、parallel(拍銳咯)并行回收醋旦、CMS并發(fā)恒水、G1

  • 串行垃圾回收器
    為單線程環(huán)境設計且只使用一個線程進行垃圾回收,會暫停所有的用戶線程饲齐。所以不適合服務器環(huán)境钉凌。 用餐-打掃衛(wèi)生-用餐。 用餐被打斷

  • 并行垃圾回收
    多個垃圾收集線程并行工作捂人,此時用戶線程也是暫停的御雕,適用于科學計算等弱交互場景,跟前臺的交互不是特別強滥搭,允許稍微停一下酸纲。 多個清潔工 打掃衛(wèi)生, 打掃的快一點瑟匆,但是客戶還是要暫停用餐闽坡。

  • 并發(fā)垃圾回收器
    生產環(huán)境,程序是不能停的愁溜。 用戶線程和辣雞收集線程可以同時執(zhí)行(不一定是并行无午,可能是交替執(zhí)行),不需要停頓用戶線程祝谚,互聯(lián)網公司多用它,適用于對響應時間有要求的場景酣衷。(前臺程序不能停)
    邊打掃邊吃飯交惯。

image.png

1.8之前主要是這三種垃圾回收器

  • G1垃圾回收器(1.8 )
    將堆內存分割成不同的區(qū)域然后并發(fā)的對其進行垃圾回收


    image.png

1.8可以用G1了, 1.8默認是并行垃圾回收,1.9默認開始是G1 java11 是更高的版本 ZGC

怎么查看默認的垃圾回收器席爽? 生產上如何配置垃圾收集器意荤? 談談對垃圾收集器的理解?

串行回收 -xx:+userSerialGC
并行回收 -xx:+UserParallelGC
并發(fā)回收 CMS(ConcMarkSweep)
G1
查看默認的垃圾回收


image.png

-xx:+printCommandLineFlags


image.png

如何認為修改默認垃圾回收

========================
七種垃圾回收(實際用六種只锻,有一種廢棄了玖像,底層源碼目前六種)

  1. Serial 收集器

Serial收集器是最基本、歷史最悠久的垃圾收集器齐饮。它是一個單線程收集器捐寥,“單線程” 的意義不僅僅意味著它只會使用一條垃圾收集線程去完成垃圾收集工作,更重要的是 它在進行垃圾收集工作的時候必須暫停其他所有的工作線程( "Stop The World" )祖驱,直到它收集結束握恳。 它會在用戶不可見的情況下把用戶正常工作的線程全部停掉。想象一下捺僻,當你結束一天的工作回到家中乡洼,喝著冰闊樂刷著副本正要打Boss,突然你的電腦說他要休息5分鐘匕坯,你會是什么感覺束昵?
存在即合理,當然Serial 收集器也有優(yōu)于其他垃圾收集器的地方葛峻,它簡單而高效(與其他收集器的單線程相比)锹雏。Serial 收集器由于沒有線程交互的開銷,自然可以獲得很高的單線程收集效率泞歉。Serial 收集器對于運行在 Client 模式下的虛擬機來說是個不錯的選擇逼侦。
它的 新生代采用復制算法,老年代采用標記整理算法腰耙。

  1. ParNew 收集器

ParNew 收集器是 Serial 收集器的多線程版本榛丢,除了使用多線程進行垃圾收集之外,其余行為(控制參數挺庞、收集算法晰赞、分配規(guī)則、回收策略等等)和 Serial 收集器完全一樣选侨。
除了支持多線程收集掖鱼,ParNew 相對 Serial 似乎并沒有太多改進的地方。但是它卻是許多運行在 Server 模式下的虛擬機的首要選擇援制,除了 Serial 收集器外戏挡,只有它能與 CMS 收集器(真正意義上的并發(fā)收集器,后面會介紹到)配合工作晨仑。ParNew單核狀態(tài)下不如Serial褐墅,多核線程下才有優(yōu)勢拆檬。
新生代采用復制算法,老年代采用標記整理算法妥凳。

  1. Parallel Scavenge 收集器

Parallel Scavenge 收集器是一個新生代收集器竟贯,也是采用復制算法+并行。聽起來和ParNew差不多對不對逝钥,那么它有什么特別之處呢屑那?
Parallel Scavenge 收集器關注點是吞吐量(CPU運行代碼的時間與CPU總消耗時間的比值)。 而CMS 等垃圾收集器的關注點更多的是縮短用戶線程的停頓時間(提高用戶體驗)艘款。停頓時間越短就越適合和用戶進行交互(響應速度快持际,可以優(yōu)化用戶體驗),而高吞吐量則可以高效的利用CPU時間磷箕,盡快完成用戶的計算任務选酗。
Parallel Scavenge 收集器提供了很多參數供用戶找到最合適的停頓時間或最大吞吐量,如果對于收集器運作不太了解的話岳枷,手工優(yōu)化存在的話可以選擇把內存管理優(yōu)化交給虛擬機去完成也是一個不錯的選擇芒填。

  1. Serial Old 收集器

Serial 收集器的老年代版本,它同樣是一個單線程收集器空繁。它主要有兩大用途:一種用途是在 JDK1.5 以及以前的版本中與 Parallel Scavenge 收集器搭配使用殿衰,另一種用途是作為 CMS 收集器的后備方案。
新生代采用復制算法盛泡,老年代采用標記整理算法闷祥。

  1. Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本。使用多線程和“標記-整理”算法傲诵。在注重吞吐量以及 CPU 資源的場合凯砍,都可以優(yōu)先考慮 Parallel Scavenge 收集器和 Parallel Old 收集器。

  1. CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器拴竹。它非常重視服務的響應速度悟衩,以期給用戶最好的體驗。栓拜。
從名字中的Mark Sweep這兩個詞可以看出座泳,CMS 收集器是一種 “標記-清除”算法實現的,它的運作過程相比于前面幾種垃圾收集器來說更加復雜一些幕与。
CMS一款優(yōu)秀的垃圾收集器挑势,
主要優(yōu)點:并發(fā)收集、低停頓啦鸣。
但是它有下面三個明顯的缺點:
1.對 CPU 資源敏感潮饱;
2.無法處理浮動垃圾;

  1. 它使用的回收算法-“標記-清除”算法會導致收集結束時會有大量空間 碎片產生诫给。
  1. G1 收集器

G1 (Garbage-First) 是一款面向服務器的垃圾收集器,開發(fā)人員希望在未來可以換掉CMS收集器香拉,它有如下特點
 并行與并發(fā):G1 能充分利用 CPU饲漾、多核環(huán)境下的硬件優(yōu)勢,使用多個 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓時間缕溉。部分其他收集器原本需要停頓 Java 線程執(zhí)行的 GC 動作,G1 收集器仍然可以通過并發(fā)的方式讓 java 程序繼續(xù)執(zhí)行吃型。
 分代收集:雖然 G1 可以不需要其他收集器配合就能獨立管理整個 GC 堆证鸥,但是還是保留了分代的概念。
 空間整合:與 CMS 的“標記--清理”算法不同勤晚,G1 從整體來看是基于“標記整理”算法實現的收集器枉层;從局部上來看是基于“復制”算法實現的。這就意味著不會產生大量的內存碎片
 可預測的停頓:這是 G1 相對于 CMS 的另一個大優(yōu)勢赐写,降低停頓時間是 G1 和 CMS 共同的關注點鸟蜡,但 G1 除了追求低停頓外,還能建立可預測的停頓時間模型挺邀,能讓使用者明確指定在一個長度為 M 毫秒的時間片段內揉忘。

image.png
jvm源碼

image.png

垃圾回收鏈接

insert a,b,c value

線上排查問題

生產環(huán)境變慢,診斷思路端铛,性能評估
整機: top
cpu: vmstat
內存: free
磁盤: df -h
硬盤IO:
網絡IO:
以上都會導致性能變慢


top

右上角泣矛,load average 有三個參數,五分鐘負載禾蚕,十分鐘負載您朽,十五分鐘負載,這三個值相加 除以3 乘 百分百换淆,如果高于百分之 60 就說明負載很高 或者 uptime 只看 load average

    1. cpu 高問題
      先用top命令找出cpu占比最高的線程
      ps -ef 或者 jps進一步定位哗总,得知是怎么樣一個后臺程序給我們惹事了
      定位到具體的線程或者代碼 ps-mp進程 id -o THREAD ,tid,time 定位到具體哪個線程出問題
      將有問題的線程ID轉換為16進制格式 printf "%x\n" 有問題線程ID
      jstack 進程ID | grep tid(16)-A60 看看線程的信息, 不管他們的堆棧倍试,就看哪有公司的名字讯屈。。易猫。 就是這個代碼惹的貨
    1. 內存高問題
    1. 如何分析dump(堆的內存鏡像)
      1.設置JVM參數-XX:+HeapDumpOnOutOfMemoryError耻煤,設定當發(fā)生OOM時自動dump出堆信息。
      2.jmap dump整個堆, jmap -dump:format=b,file=jmap.info PID
      工具: mat(eclipse) jhat(jdk自帶的)


      image.png
    1. 經常用哪些分析工具

jinfo java配置相關
jmap 內存映像工具
jstat 統(tǒng)計信息監(jiān)視

*常用調優(yōu)參數
boolean 類型
-XX:[+-]<name> 表示是否啟用jvm的某個參數
非boolean類型
XX:<name> = <value> 表示name屬性的值為value
Xms:初始堆內存大凶纪恰(-XX:initialHeapSize)
-Xmx:最大堆內存大泄(-XX:MaxHeapSize)
-XX:NewRatio 新生代老年代之間的比例
-XX:MetaSpaceSize MetaSpace大小
打印參數相關的
-XX:+PrintGCDetails 顯示gc詳細信息
-XX:+PrintHeapAtGC 發(fā)生gc時打印出堆棧信息
-XX:+PrintTenuringDistribution 打印出對象的分布情況

gc常用參數

jps:查看java的相關進程
jinfo:產看正在運行的jvm的參數
jstat:查看jvm的統(tǒng)計信息(包括類加載信息,垃圾收集信息攘已,jit編譯信息)

  • 如何選擇垃圾回收器
    1.優(yōu)先調整堆的大小炮赦,讓jvm自己選擇
    2.內存小于100M,使用串行垃圾收集器
    3.單核沒有停頓時間的要求样勃,使用串行吠勘,或者jvm自己選擇
    4.允許停頓時間超過1s 選擇并行或者jvm自己選擇
    5.若響應時間重要性芬,使用并發(fā)收集器

jdk1.7 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.8 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.9 默認垃圾收集器G1

設定堆內存大小-Xmx:堆內存最大限制。設定新生代大小剧防。 新生代不宜太小植锉,否則會有大量對象涌入老年代-XX:NewSize:新生代大小-XX:NewRatio 新生代和老生代占比-XX:SurvivorRatio:伊甸園空間和幸存者空間的占比設定垃圾回收器 年輕代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC

-Xms:初始堆大小,默認為物理內存的1/64(<1GB)峭拘;默認(MinHeapFreeRatio參數可以調整)空余堆內存小于40%時俊庇,JVM就會增大堆直到-Xmx的最大限制
-Xmx:最大堆大小,默認(MaxHeapFreeRatio參數可以調整)空余堆內存大于70%時鸡挠,JVM會減少堆直到 -Xms的最小限制
-Xmn:新生代的內存空間大小辉饱,注意:此處的大小是(eden+ 2 survivor space)。與jmap -heap中顯示的New gen是不同的拣展。整個堆大小=新生代大小 + 老生代大小 + 永久代大小彭沼。在保證堆大小不變的情況下,增大新生代后,將會減小老生代大小备埃。此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個堆的3/8姓惑。
-XX:SurvivorRatio:新生代中Eden區(qū)域與Survivor區(qū)域的容量比值,默認值為8瓜喇。兩個Survivor區(qū)與一個Eden區(qū)的比值為2:8,一個Survivor區(qū)占整個年輕代的1/10挺益。
-Xss:每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K乘寒。應根據應用的線程所需內存大小進行適當調整望众。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進程內的線程數還是有限制的伞辛,不能無限生成烂翰,經驗值在3000~5000左右。一般小的應用蚤氏, 如果棧不是很深甘耿, 應該是128k夠用的,大的應用建議使用256k竿滨。這個選項對性能影響比較大佳恬,需要嚴格的測試。和threadstacksize選項解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話:"-Xss is translated in a VM flag named ThreadStackSize”一般設置這個值就可以了于游。
-XX:PermSize:設置永久代(perm gen)初始值毁葱。默認值為物理內存的1/64。
-XX:MaxPermSize:設置持久代最大值贰剥。物理內存的1/4倾剿。

什么是堆外內存

JDK的ByteBuffer類提供了一個接口allocateDirect(int capacity)進行堆外內存的申請,底層通過unsafe.allocateMemory(size)實現蚌成。
對內 內存 就是我們常說的 堆 新生代+老年代

內存對象分配在Java虛擬機的堆以外的內存前痘,這些內存直接受操作系統(tǒng)管理(而不是虛擬機)凛捏,這樣做的結果就是能夠在一定程度上減少垃圾回收對應用程序造成的影響。使用未公開的Unsafe和NIO包下ByteBuffer來創(chuàng)建堆外內存芹缔。
優(yōu)點:
1.減少了垃圾回收坯癣,使用堆外內存的話,堆外內存是直接受操作系統(tǒng)管理( 而不是虛擬機 )最欠。這樣做的結果就是能保持一個較小的堆內內存坡锡,以減少垃圾收集對應用的影響。

  1. 提升復制速度(io效率)
    堆內內存由JVM管理窒所,屬于“用戶態(tài)”;而堆外內存由OS管理帆锋,屬于“內核態(tài)”吵取。如果從堆內向磁盤寫數據時,數據會被先復制到堆外內存锯厢,即內核緩沖區(qū)皮官,然后再由OS寫入磁盤,使用堆外內存避免了這個操作实辑。

http 和 hppts關系

http是超文本傳輸協(xié)議捺氢,信息是明文傳輸(內容可能被竊聽),https則是具有安全協(xié)議的ssl加密傳輸協(xié)議剪撬。
https協(xié)議需要到ca申請證書摄乒,一般免費的證書很少,需要交費残黑。 https更安全
http和https使用的是完全不同的連接方式用的端口也不一樣馍佑,前者是80,后者是443.
http + 加密 + 認證 + 完整性保護 = https

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末梨水,一起剝皮案震驚了整個濱河市拭荤,隨后出現的幾起案子,更是在濱河造成了極大的恐慌疫诽,老刑警劉巖舅世,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異奇徒,居然都是意外死亡雏亚,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門逼龟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來评凝,“玉大人,你說我怎么就攤上這事腺律∞榷蹋” “怎么了宜肉?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翎碑。 經常有香客問我谬返,道長,這世上最難降的妖魔是什么日杈? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任遣铝,我火速辦了婚禮,結果婚禮上莉擒,老公的妹妹穿的比我還像新娘酿炸。我一直安慰自己,他們只是感情好涨冀,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布填硕。 她就那樣靜靜地躺著,像睡著了一般鹿鳖。 火紅的嫁衣襯著肌膚如雪扁眯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天翅帜,我揣著相機與錄音姻檀,去河邊找鬼。 笑死涝滴,一個胖子當著我的面吹牛绣版,可吹牛的內容都是我干的。 我是一名探鬼主播歼疮,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼僵娃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腋妙?” 一聲冷哼從身側響起默怨,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骤素,沒想到半個月后匙睹,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡济竹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年痕檬,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片送浊。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡梦谜,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情唁桩,我是刑警寧澤闭树,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站荒澡,受9級特大地震影響忘分,放射性物質發(fā)生泄漏描焰。R本人自食惡果不足惜该酗,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一悠砚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧米奸,春花似錦昼接、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膨疏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钻弄,已是汗流浹背佃却。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窘俺,地道東北人饲帅。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像瘤泪,于是被迫代替她去往敵國和親灶泵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容

  • --- layout: post title: "如果有人問你關系型數據庫的原理对途,叫他看這篇文章(轉)" date...
    藍墜星閱讀 788評論 0 3
  • 索引 數據庫中的查詢操作非常普遍实檀,索引就是提升查找速度的一種手段 索引的類型 從數據結構角度分 1.B+索引:傳統(tǒng)...
    一凡呀閱讀 2,918評論 0 8
  • 今天看到一位朋友寫的mysql筆記總結惶洲,覺得寫的很詳細很用心,這里轉載一下膳犹,供大家參考下恬吕,也希望大家能關注他原文地...
    信仰與初衷閱讀 4,730評論 0 30
  • 1.棧是一種線性結構(常見有:線性表,棧须床,隊列等)铐料,具有先進后出的特征,也就是后進先出。 2.Java單繼承钠惩,多實...
    Jennyni1122閱讀 1,364評論 1 0
  • 我是玉玲成柒凉,一名25歲的花季少女,(嘔妻柒。扛拨。。小編表示無力吐槽)举塔,現在人們的平均年齡在120歲左右绑警,我25歲...
    雨夜風瀟瀟閱讀 172評論 0 1