簡歷自我介紹介紹一下項目,以及難點亮點科研項目Git 、SVN、MAVENGit和 SVN的區別,有哪些優點?Git 下 init
以後生成的.git文件夾是幹嗎的?Maven怎麼解決衝突?Maven依賴原則:衝突緣由和解決JAVA語言Java 集合各類集合的簡介及區別聯繫Hash表解決衝突的方法Hashmap中的hash(key)方法resize()的過程HashMap多線程下出現的問題ConcurrentHashMap 如何擴容?Java NIOBufferChannelSelectorSpringMVC和MyBatisSpringMVC運行流程攔截器@Resource
和 @Autowaired
SpringIOCaop事務singleton 和 prototype的做用範圍(使用scope指定)MyBatis簡介動態代理簡介動態代理的原理 ***經常使用標籤動態SQL事務支持和緩存機制具體問題垃圾回收機制垃圾回收算法垃圾回收的時機垃圾收集器新生代收集器老年代收集器G1收集器類加載機制虛擬機的方法調用解析分派JVM 鎖優化***JVM編譯過程***多線程,併發終止線程的方式***J.U.C 包volatilesychronized 原理ThreadLocalAQS(AbstractQueuedSychronizer)信號量 (Semaphore) ['seməfɔː]CountDownLatchAtomicIntegerFuturefinal 域獲取線程dump文件線程的喚醒與阻塞線程池簡介四種線程池Executors.newCashedThreadPool()Executors.newFixedThreadPool()Executors.newScheduledThreadPool()Executors.newSingleThreadExecutor()若是你提交任務時,線程池隊列已滿,這時會發生什麼線程池的大小幾種鎖如何在兩個線程之間共享數據寫一段 sychronized 能產生死鎖的代碼異常系統迭代器和比較器Iterator和IterableComparable和ComparatorJava 基礎String 相關重載和重寫泛型怎麼實現sychronized 修飾static方法和普通方法的區別具體問題數據結構和算法基礎樹計算機網絡http 和 https長鏈接和短連接HTTP/1.0、HTTP/1.一、HTTP/2.0HTTP/1.0和HTTP/1.1HTTP/1.1 和 HTTP/2.0狀態碼相關TCP/IP分層和OSI分層TCP/IP分層OSI 分層TCP和UDP的區別,TCP如何保持可靠性傳輸TCP相關重點TCP如何保證可靠傳輸TCP的擁塞控制慢開始和擁塞避免快重傳和快恢復UDP協議session 和 cookieHttp中 Header信息操做系統進程進程之間的調度方式處理機調度的層次調度算法進程間的通訊方式死鎖死鎖產生緣由死鎖產生必要條件死鎖預防和避免的算法存儲器管理連續行存儲管理方式離散型存儲管理方式頁式管理方式尋址快表TLB段式管理方式尋址段、頁式的區別段頁式管理方式虛擬存儲器置換算法數據庫範式第一範式第二範式第三範式mysql爲何用B+樹mysql什麼狀況下會觸發表鎖樂觀鎖、悲觀鎖讀鎖、寫鎖、表鎖、行級鎖數據庫事務的四種隔離級別ACID事務ACID事務實現原理InnoDB和MyISAMInnoDBMyISAM比較和討論索引的實現方式在A、B、C上創建索引,可用與不可用問題***類別鏈接(join)操做設計模式幾種關係繼承實現依賴關聯聚合組合幾個原則開閉原則單一職責原則依賴倒轉原則迪米特法則幾種單例模式優化技術前端緩存CDN後端緩存RedisNoSql : Not only Sql應用場景數據結構事務持久化底層實現之跳錶基於Redis實現分佈式鎖和分佈式任務隊列負載均衡MySql優化Sql及索引數據庫表結構系統配置硬件分佈式和大數據負載均衡的方法一致性Hash算法HDFS文件系統的工做原理組成部分和相關概念寫操做讀操做Hadoop和SparkMap and ReduceHadoop的數據流(運行簡要過程)容錯機制SparkHadoop 和 Spark的比較開放式問題海量數據海量數據排序海量日誌數據,提取出某日訪問百度次數最多的那個IP給40億個不重複的unsigned int的整數,沒排過序的,而後再給一個數,如何快速判斷這個數是否在那40億個數當中?10億條數據取中位數字符串匹配前綴樹html
init
以後生成的.git文件夾是幹嗎的?短路優先:會優先依賴路徑短的版本。前端
先聲明先優先。java
例如A依賴B,B依賴C,C依賴jar包X1;A依賴D,D依賴jar包X2,假設X1和X2是相同的jar包,只有版本不同,此時A優先依賴X2,可是,有可能咱們須要A依賴Jar包X1,此時咱們可使用denpendency:tree
來查看依賴樹,並發現衝突版本;在引用D的時候可使用<exclusion>
來排除X2。node
Java 集合在util([ju til])包下,主要包括Collection和Map兩個接口。Collection又包括Set,List,Quene三個接口。List主要包括ArrayList,LinkedList,Vector(Stack 繼承了Vector)等實現類,LinkedList同時實現了Queue接口,Queue中包含了一些封裝的操做(add offer || remove poll element peek)。Queue還有一個實現類是PriorityQueue,此類底層使用一個堆實現的,每次都返回最小值/最大值(能夠實現Comparator重寫compare實現)。此外,還有一個Set接口,Set接口底層實現是根據Map的,採用適配器模式。add方法即Map接口中的put(e,null)。如下介紹下Map接口。Map接口包含Hashtable、HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap等,TreeMap同時實現了SortedMap,爲有序的Map。mysql
ArrayList:底層用數組實現,插入,添加,獲取的的時間複雜度都爲。刪除的時間複雜度爲。有兩個標誌數據,capacity爲容量,size爲當前元素的數量。當容量不夠擴容時使用Arrays.copyOf進行復制操做,而且容量擴展爲原來容量的1.5倍。DEFULT_CAPACITY
爲10。copyOf 方法調用了System.arraycopy方法,該方法是一個native方法。git
LinkedList:底層用雙向鏈表實現,插入,獲取須要時間複雜度,添加、刪除須要的時間複雜度。程序員
Vector:與ArrayList的區別在於Vector是線程安全的,(能夠用Collections.sychronizedList()
包裝ArrayList)單線程狀況下效率較低,同時在add操做時,ArrayList擴容爲1.5倍,Vector擴容爲2倍。算法
Hashtable:Hashtable是一個線程安全的類,其方法都是sychronized的。其與HashMap的關係有點像Vector與ArrayList的關係。spring
HashMap:其底層爲Hash表,(數組和單鏈表的集合),容量能夠被用戶指定,默認爲1<<4(16)(爲2的冪的緣由在於以後的hash操做返回index,使用的是&的位運算,以提升效率),負載因子load_factor默認爲0.75,當hash表中的元素數量大於總容量的0.75時,就要對hash擴容,擴展爲原容量的兩倍,並對原來的元素進行從新hash(rehash)。數組叫作bucket(桶),單鏈表的元素叫作entry(元素)。sql
LinkedHashMap與HashMap的不一樣之處在於使用了雙向鏈表維護了元素之間的插入順序。便是,插入時既要插入對應的桶中,又要插入到雙向鏈表的尾部。以此保證迭代順序跟插入順序相同。
TreeMap 底層用一個紅黑樹實現,從而實現了數據的有序。在迭代元素時,按有序的順序迭代。其插入和獲取的時間複雜度均。紅黑樹的本質是一個2-3查找樹,黑連接爲2-3查找樹中的普通連接,紅連接爲其中的3節點拆分後的連接,節點的顏色爲連接該節點連接的顏色。根節點必須爲黑連接,紅連接不能連續,大小爲的紅黑樹高度不會超過,接近平衡。在插入或刪除操做時,要使用左右旋以保證其知足紅黑樹性質。
ConcurrentHashMap:與HashMap不一樣的是,ConcurrentHashMap是線程安全的,爲分段結構,segment中含有Hashtable,每次對一個segment中的Hashtable加鎖進行put、delete等操做,而不是鎖整張表,segment的默認大小仍然是16,意味着能夠有16個線程同時操做該ConcurrentHashMap。1.8對ConcurrentHashMap進行了改進,拋棄了以前臃腫的segment數組,改用node + CAS + sychronized + 紅黑樹來實現。採用對數組元素進行加鎖,從而實現了更細粒度的鎖。
CopyOnWriteList (寫時複製List)其發佈了一個事實上不可變的對象,因爲不可變,容許多個線程訪問該容器,每當修改容器時,都要建立一個副本進行修改而後再發布。因此最好在迭代訪問操做遠遠多於修改操做的時候才使用該同步容器。
BlockingQueue
開放定址法: ,即在原來地址的基礎上加上或減去一個數,再進行定地址的操做。理論上可使用全部的存儲空間,可是容易形成對地址的爭奪,產生二次彙集,即在處理同義詞的衝突的過程當中又增長了非同義詞的爭奪 。
再哈希:使用不一樣的hash函數再次進行hash操做。不易產生彙集,可是增長了計算時間。
鏈地址法:將衝突的元素進行用一個鏈表進行存儲
公共溢出區
java中的hash操做,首先要對hash的key值進行取模操做,具體實現爲,將hash值與hash表的長度減一進行與操做。這樣操做的結果就是將高位所有去掉,將低位保存,這也是hash表的大小要取值爲2的整數次冪的緣由。在獲得低位的值以後,因爲捨去了高位的信息,因此衝突的機率較大。因此jdk中採起了與高16位(int 爲4字節32位)進行異或的操做,以指望引入高位的信息。因此最終的實現爲:
static final int hash(Object key){
int h;
return (k==null)?0:(h = key.hashCode())^(h >>> 16);
}
模運算則使用return h & (length-1);
完成。
put的覆蓋問題:存在兩個線程添加一個元素,entry相同,因爲e.next不一樣步,在拉鍊時出現覆蓋問題。
put以後致使get無限循環:發生在擴容中,當一個線程被掛起時,另外一線程重組鏈表後可能發生循環訪問的問題。記不清可見
最主要的三個類是Buffer,Channel和Selector。與傳統的IO比較主要有亮點不一樣。
IO是面向流的,而NIO是面向緩衝區的。傳統的IO面向流意味着每次從流中讀取一個或者多個字節,這些數據沒有緩衝在任何一個地方。若是要先後移動流中的數據,要先將其還存在一個緩衝區中。而面向緩衝區的NIO有先天的先後操縱數據的優點。
IO是阻塞的,便是,當調用read()或者write()方法時,當沒有數據讀出或者沒有數據寫入時,線程將是阻塞的;不一樣的是NIO是非阻塞的,當一個線程發送讀的請求時,其只能讀取當前可返回的數據,若是當前沒有可用的數據,就什麼都不會返回。寫請求時,不用等到所寫的數據要所有寫入時,線程就能夠去作其餘的事情。線程一般將非阻塞的IO用於在其餘的通道上進行讀寫操做,因此一個IO能夠管理多個通道。
每種類型都有對應的Buffer,可是經常使用的Buffer爲ByteBuffer
和CharBuffer
。Buffer
具體的實例由XXXBuffer.allocate(int capacity)
來獲取。ByteBuffer
還有一個子類MappedByteBuffer
,這個子類一般經過Channel
的map()
方法返回,該緩衝區將磁盤的所有或部份內容映射到內存(虛擬內存)。讀寫性能較高。
其有幾個比較重要的標誌,capacity,limit和position。其中capacity是指緩衝區的容量,由allocate初始化的時候指定。limit是指讀取和寫入時的上界限,position是指讀取和寫入時下一個位置。讀取以前,調用flip()
使position回到0 (讀取的下一位置),limit回到定位到最後一個元素的下一位置 (讀取的上界限),capacity不變。寫入時,首先調用clear()
方法,此時緩衝區中的元素並無真正被clear,使用buffer.get(int pos)
仍然能獲取到緩衝區的內容,只是將position定位到0,將limit定位到capacity的位置,寫入時,將覆蓋原緩衝區的內容。
通常須要用流的getChannel()
方法來初始化Channel,例如new FileInputStream(f).getChannel()
,new FileOutputStream(f).getChannel()
,new RandomAccessFile(f,"rw").getChannel()
。讀取文件到Buffer裏能夠用channel
的read()
方法或channel
的map(***)
方法,寫入則使用channel
的write()
方法。
使用read()
的具體形式爲
...;
File f = new File("afile.txt");
FileChannel channel = new FileInputStream(f).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(f.length());
channel.read(buffer);
...;
使用map()
的具體形式爲
...;
File f = new File("afile.txt");
FileChannel channel = new FileInputStream(f).getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
...;
selector使用單線程處理多個channel,當應用打開了多個channel且每一個channel的吞吐量不是很大時,使用channel就比較方便。使用selector分以下的幾步:
建立:使用Selector selector = Selector.open()
將channel註冊到selector:channel.register(selector, **)
,該方法可返回一個SelectorKey,表明該channel在selector中的"代號"。與selector一塊兒使用時,channel必定處於非阻塞模式下。因此須要提早設置channel.configureBlocking(false)
。
使用selector幾個select的重載方法測試通道有多少已經準備好了:
int select()阻塞到至少有一個對象在通道上準備好了。
int select(long timeout)最長會阻塞timeout毫秒
int selectNow()不會阻塞,無論什麼狀況會直接返回,若是自上次調用選擇操做以來,沒有就緒的通道則直接返回0。
調用select方法以後,若是有返回值,則說明已經有selector就緒了。此時能夠經過selectedKey來選擇已選擇鍵集中就緒的通道。使用Set<SelectionKey> selectedKey = selector.selectedKeys();
,以後在獲得的SelectionKey的Set中能夠經過SelectionKey提供的方法來操做channel。獲得Channel的基本方法爲selectionKey.channel()
。
用戶向服務端發送請求,被DispatcherServlet (調度員Servlet)截獲,而後交由HandlerMapping處理,得到Handler對象及其對應的攔截器,以HandlerExecutionChain返回給DispatcherServlet,DispatcherServlet根據返回的Handler選擇合適的HandlerAdapter處理,該適配器調用對應Handler的實際處理請求的方法(例如login)。接下來執行相應的Handler方法,並返回一個ModelAndView對象,這個對象中包含視圖名稱和數據對象,選擇一個合適的ViewResolver對象返回給DispatcherServlet,渲染View並返回給客戶端。
實現 HandlerInterceptorAdapter接口,其中preHandler在攔截以前執行,postHandle在攔截以後執行;在springmvc-servlet.xml中須要定義<mvc:interceptors>
, <mvc:mapping>
指定須要過濾的路徑,bean
指定攔截器類。
@Resource
和 @Autowaired
@Resource
是屬於javax
的,默認按名稱進行裝配,當找不到名稱匹配的類型才使用類型進行裝配;@Autowaired
是屬於spring
的,默認按照類型進行裝配。
注入方式:設值注入(setter)和構造注入;設值注入 須要一個setter方法和屬性,在配置文件中用標籤<property>
指定要注入的類。構造注入指在類的構造器中注入屬性,須要在配置文件中使用<constructor-arg ref="XXXClass">
來進行注入。
Spring核心組件:BeanFactory
,其有一個子接口ApplicationContext
所以也稱爲Spring上下文。經過getBean來獲取對象實例。
Bean的做用域:經過scope="XXX"
來指定Bean的做用域
singleton:單例模式,使用singleton定義的Bean在整個IoC容器中將只有一個實例
prototype:原型模式,每次經過容器的getBean獲取prototype定義的Bean都將產生一個新的實例
request:每次HTTP請求產生一個新的實例
session: 每次Http session產生一個新的實例
global session:每次全局的HTTP session對應一個Bean實例
默認狀況下,Spring IoC容器的Bean做用域爲singleton。
在不改動原有方法時對原有類進行加強。源於設計模式的代理模式,使用JDK提供的動態代理技術或CGLIB的動態代理技術。默認使用JDK動態代理技術,若是要改成CGlib,則要指定<aop:aspectj-autoproxy proxy-target-class="true" />
。
JDK動態代理:實現InvocationHandler
接口,重寫invoke()
方法,須要對接口實現代理。
CGLIB:經過CGlib原理是爲被代理的類生成一個子類,重寫被代理的方法;實現MethodInterceptor
接口,重寫intercept()
方法。
比較:JDK動態代理必需要實現接口,對接口進行動態代理加強;而CGLIB利用繼承關係,若是被代理類或方法被final修飾,則不能被代理。
使用@Aspect
定義一個切面,切面中定義對某一包下的方法進行各類類型的加強,在切面中使用@Before()
, @AfterReturning()
,@AfterThrowing()
,@After()
進行對原有方法的加強 。
在@Aspect
中使用@Pointcut
定義一個切點(要加強的方法),在加強方法中用@Before(pointcut="XXX.myPointcut()")
來爲加強方法指定該切點。
使用JointPoint類型來訪問要加強方法的參數等信息
聲明式事務: @Transactional
等
編程式事務:使用TransactionManager 或 TransactionTemplete 兩個類
事務底層實現原理???????????????
事務傳播機制???????????
singleton 單例模式,在整個 SringIoC 容器中,使用singleton的bean將只擁有一個實例
prototype原型模式,每次經過getBean方式得到Bean的時候,都將產生一個新的實例。
主要是把sql語句和代碼分開,以加強項目的可維護性。通常將sql寫在xml文件中,被叫作mapper文件,一個BEAN對應一個mapper文件,其中有對該BEAN的增刪改查。涉及了兩個比較重要的類,SqlSessionFactory和SqlSession。其中增刪改查的方法均源於SqlSession。也可使用Mapper接口的動態代理對象來訪問數據庫,invoke方法中只根據package+Mapper+method全限定名肯定執行的方法,即一個mapper文件對應一個Mapper接口,接口的一個方法對應了一個sql語句。方法實際上底層仍然調用的是SqlSession的方法。
爲了加強target,代理類須要繼承一個InvocationHandler接口,實現invoke方法。以Proxy.newProxyInstance(ClassLoader, Class(Interface), InovationHandler)
獲取代理對象。只能爲接口對象獲取代理。MyBatis的Mapper接口由於沒有實現類,所以直接運行代理方法而沒有對所謂的target加強。
首先是config文件中的標籤:根標籤爲<configuration>
,其中較爲重要的標籤爲<settings>
:設置mybatis的行爲,制指定日誌實現、緩存、懶加載等等。<environments>
配置mybatis的環境變量,即爲數據源配置。須要配置dataSource和事務管理器等。<typeAliases>
指定類型的別名,爲其設置一個方便的名字。<mappers>
標籤指定了每張數據表所屬於的mapper映射器。
Mapper XML文件中的標籤:增(add),刪(delete), 改(update), 查(select)。<resultMap>
是mybatis中比較強大的標籤,能夠將數據庫中選擇的column(column屬性)指定成須要的對象屬性(property),在關聯映射中這個屬性用的尤爲多。關聯其餘表的一行記錄時,在resultMap
中使用<association>
標籤,用column
指定對應的外鍵,select
指定定義好的查詢,javaType
指定返回的類型。關聯其餘表的多行記錄時,用<collection>
標籤,除上面的屬性能夠指定外,還可使用fetchType="lazy"
指定查詢模式爲懶加載。
爲適應實際開發中常常碰到的拼接sql的狀況,常使用MyBatis提供了一些標籤提供對動態sql的支持。if用來判斷條件,choose配合when、otherwise用來判斷多個條件,where能夠拼接後面的where條件而不須要加where 1=1
這種語句,set可用在更新語句中,foreach可用於where xx in
的查詢語句;bind能夠從OGNL表達式中建立一個變量並將其綁定到上下文。
MyBatis經過TransactionFactory來返回事務管理對象。其中JdbcTransaction直接使用JDBC的提交和回滾事務管理機制。(java.sql.Connection中的commit()和rollback()來實現。)ManagedTransaction的事務管理交給容器管理。
MyBatis有兩級緩存機制。
SqlSession級別,構造SqlSession的時候使用HashMap用於存儲緩存數據,不一樣的SqlSession存儲的緩存數據之間是不影響的。同一個SqlSession對象執行兩次相同的查詢操做,第二次能夠直接從內存中拿數據而不須要再次查表。若是SqlSession執行了DML操做並提交到了數據庫,MyBatis將會清空當前的緩存,避免髒讀。MySql默認開啓。
mapper級別,多個SqlSession使用同一個Mapper的查詢操做,獲得的數據會緩存在二級緩存區域,一樣使用HashMap進行數據存儲。可是緩存是多個SqlSession共享的,做用域是mapper的同一個namespace。不一樣的SqlSession執行兩次相同的namespace下的sql語句,第二次執行的能夠直接額從內存中拿數據。默認沒有開啓,須要在setting參數中配置開啓二級緩存。使用以下配置:
<settings>
<setting name="cacheEnable" value="true"/>
</settings>
在mapper.xml中能夠配置二級緩存的屬性。eviction收回策略LRU、FIFO、SOFT、WEAK;size:緩存大小;flushInterval刷新間隔,默認沒有;readOnly:讀寫屬性,默認false。
MyBatis中${}和#{}的區別? MyBatis基於JDBC封裝,#{}是預編譯處理範疇的(PreparedStatement);而${}是字符串替換。MyBatis在運行的時候在執行#{}的sql語句時,會首先建立一個prepareStatement, 而後再將參數的值使用setInt設置。
主要分新生代和老年代的垃圾回收。新生代採用複製算法 ,即一塊較大的Eden(['i:dən])區域和兩塊較小的survivor區域,對象優先在Eden區域中分配,當Eden區域滿了,進行一次複製回收,由於java對象大部分的對象都具備朝生夕滅的特色,因此survivor區域很小(Minor GC);老年代採用標記整理算法 (由於老年代的對象存活率很高),即將還在使用的對象移到一邊,以在回收的同時產生連續的空間。(Full GC) 還有一種回收算法叫作標記清除算法 ,效率不高 什麼對象能夠被回收?
引用計數法:每有一個對象被引用,就將該對象的引用計數加一,引用失效時,就將引用計數減一,當引用計數爲0的時候,代表該對象能夠被回收。可是難以解決對象循環引用的問題。
可達性分析算法:經過一系列「GC Root」的對象做爲起始點,從這個節點向下搜索引用該節點的對象,一條路徑稱爲一條引用鏈,若是一個對象到GC Root沒有任何可達的引用鏈,則該對象就是能夠被GC的。可做爲GC Root的對象包括(1)虛擬機棧中引用的對象(2)方法區中類靜態屬性引用的對象(3)方法區中引用的常量(4)Native方法引用的對象。 幾條原則:
對象優先在Eden區分配
大對象直接進入老年代:大對象指須要連續大塊內存的java對象,例如很長的字符串和很大的數組(寫程序應該避免使用朝生夕滅的短命大對象);因爲申請不到連續的足夠空間,即便內存區域還含有不少空閒區域,仍然須要進行垃圾回收以獲取足夠的連續空間來安置他們。因此大對象直接進入老年代能夠避免較爲頻繁的GC。
長期存活的對象進入老年代:對象的年齡計數器Age,每熬過一次Minor GC,則該對象的年齡就增長一歲。默認大於15歲的對象進入老年代。動態對象年齡判斷:相同年齡的對象大小的總和大於Survivor空間的一半,年齡大於等於該年齡的對象能夠直接進入老年代。
通常在Eden區不夠分配的時候,JVM將出發Minor GC的過程;接着會發生空間分配擔保的過程。
空間分配擔保(Minor GC 和 Full GC):在Minor GC以前,會先檢查老年代最大可用連續空間是否大於新生代全部對象空間,Minor GC就是安全的。若是不成立,會檢查是否容許擔保失敗,若是容許,就檢查連續空間是否大於歷次晉升到老年代的對象的大小的平均值 ,若是是,就進行一次Minor GC,同時風險是老年代已經存不下了,這時就要再進行一次Full GC,不然進行一次Full GC。若是不容許擔保失敗,就要在Minor GC前先進行Full GC。
Serial收集器:如其名字,在垃圾回收的時候會暫停全部的線程(Stop the world)
ParNew收集器:其實就是serial收集器的多線程版本,使用多個線程進行垃圾回收,使用複製算法
Paralell Scavenge收集器:並行收集器且使用複製算法進行垃圾回收,與ParNew關注點不同,關注點在於吞吐量,能夠設置一個可控的吞吐量。吞吐量 = 運行用戶代碼的時間 / (運行用戶代碼的時間 + 垃圾收集時間),能夠爲垃圾收集器設置自適應值,讓垃圾回收器動態調整參數以達到最大的吞吐量。不能夠與CMS收集器配合使用,只能與Parallel Old配合使用
Serial Old 收集器 Serial 收集器的老年代版本
Parallel Old 收集器:是Parallel Scavenge的老年代版本,使用標記整理算法
CMS收集器:是一種以最短回收停頓時間爲目標的收集器,其基於的是標記清除算法(注意不是標記--整理算法)。分爲如下幾個步驟:初始標記-->併發標記-->從新標記-->併發清除。其中初始標記和從新標記都是須要Stop the world的。初始標記記錄一下GC Roots直接關聯的對象,併發標記階段是進行GC Root Tracing的過程,從新標記算法是爲了修正在併發標記階段用戶程序繼續進行形成的標記變更。缺點:對CPU敏感;沒法處理浮動垃圾,所謂浮動垃圾,是指垃圾收集階段運行用戶程序所產生的垃圾,這些垃圾須要下一次GC才能清理;基於「標記--清除」算法,因爲碎片過多可能會過早的產生Full GC。
G1收集器將Java堆劃分爲多個大小相等的獨立區域(Region),雖然還有新生代老年代的概念,可是再也不是物理隔離的了,他是一部分Region的集合。其步驟分爲初始標記->併發標記->最終標記->篩選回收,前兩個步驟與CMS是類似的,初始標記階段標記GCRoot,而且記錄正確可用的Region信息,下一段運行程序能在可控的Region中建立對象;在篩選回收階段對各個Region進行回收價值和成本的排序,回收特定的Region區域,以此達到時間可控 。除了時間可控,G1採用「標記--整理」算法,避免了過多碎片的產生。
雙親委派模型及其意義:啓動類加載器(Bootstrap ClassLoader)在最頂層,接着是擴展類加載器(Extension ClassLoader)和應用程序類加載器(Application ClassLoader),最底層是用戶定義的類加載器。雙親委派模型要求在有類加載任務的時候首先檢查父類加載器是否能夠加載該類,每個類的加載都要首先傳送到頂層的啓動類加載器。當父類加載器不能完成加載時,才進行子類加載器的加載。這樣作的意義在於類加載器具備了優先級關係。例如Object類在啓動類加載器中加載,不管哪一個類加載器加載,最終都交由啓動類加載器加載,所以Object類在各類類加載器狀況下都是同一個類。若是不採起這種模型,將產生多個同名但不一樣的類,java的基礎運行得不到保障。在當前JDK下,用戶自定義與rt.jar中類庫重名的類是沒法被加載運行的。
類加載過程:只有在加載階段用戶能夠經過自定義類加載程序參與以外,其他動做徹底由虛擬機主導和控制
加載
經過類的全限定名來獲取此類的二進制字節流
將字節流的靜態存儲結構轉換爲方法區中的運行時數據結構
在內存中生成一個表明該類的Class對象,做爲方法區這個類的各類數據的訪問入口。
連接
驗證:爲了確保Class文件字節流中包含的信息符合當前虛擬機的須要,且不會危害虛擬機的安全
文本格式驗證:字節流是否符合Class文件格式的規範(例如魔數開頭等)
元數據驗證:進行語義的分析,以保證符合Java語言的語法規範
字節碼驗證:判斷語義是否合法,符合邏輯
符號引用驗證:可否經過符號引用找到相應的類、方法等
準備:是爲類變量 分配內存並設置類變量初始值,這些變量所使用的內存都將在方法區 進行分配。此時分配初值的變量僅包含類變量,便是用static修飾的變量;此時分配初值只是分配一個默認值,好比int類型的賦值爲0,真正的初值(程序員定義的初值)將在類初始化的階段纔會進行賦值;可是若是使用了final修飾類變量,則類變量將在準備階段被附上初值。
解析:解析階段是將常量池的符號引用轉換爲直接引用的過程;
初始化:在初始化階段,會根據程序員經過程序制定的主觀計劃去初始化類變量和其餘資源。執行<clinit>
全部的方法調用在Class文件裏都是一個常量池中的符號引用。在類加載的解析階段,會將其中一部分符號引用轉化爲直接引用,這種解析能成立的前提是:方法在程序真正運行以前就有一個可肯定的調用版本,而且這個版本在運行期間是不可改變的。這類方法調用稱爲解析。
Java語言中「編譯期可知,運行期不可變」的方法主要包含靜態方法和私有方法兩大類,前者和類型直接相連,後者外部不可訪問,這兩類方法決定了他們不可能經過繼承或者別的方式重寫其餘版本,所以都適合在類加載階段進行解析。與之相對應的,java虛擬機裏提供了5條方法調用字節碼指令,分別以下。
invokestatic
:調用靜態方法。
invokespecial
:調用實例構造方法,私有方法和父類方法。
invokevirtual
:調用全部的虛方法。
invokeinterface
:調用接口方法,會在運行期肯定實現此接口的對象。
invokedynamic
: 如今運行期動態解析出調用點限定符所引用的方法,再執行該方法。
只要能被invokestatic
和 invokespecial
指令調用的方法,均可以在解析階段肯定惟一的調用版本,因此符合這個條件的有靜態方法、私有方法、實例構造器、父類方法4類,在類加載過程當中就會把符號引用轉換爲直接引用。這些方法能夠稱爲非虛方法 ,其餘方法成爲虛方法 (除去final方法)。雖然final方法是經過invokevirtual
來調用的,可是因爲其沒法被覆蓋,無需對其進行多態選擇,因此是一種非虛方法。
靜態分派:與重載過程緊密相關,使用那個重載版本,取決於傳入參數的數量和數據類型,虛擬機在重載時經過參數的靜態類型(外觀類型)而不是實際類型做爲斷定依據。靜態類型是在編譯期可知的,所以在編譯階段,javac編譯器會根據參數的靜態類型來決定使用哪一個版本。靜態分派發生在編譯階段。
動態分派:與重寫過程緊密相關,invokevirtual
指令第一步就在運行期會肯定元素所指向對象的實際類型。這種在運行期間根據實際類型肯定方法執行版本的分派過程成爲動態分派。
單分派和多分派。方法的接受者和方法的參數統稱爲方法的宗量。有多個宗量決定的分派叫多分派,靜態分派屬於多分派,由於由方法的參數和靜態類型共同決定。而動態分派屬於單分派,由於只有接受者的實際類型決定了選擇哪一個方法。
只保證可見性,保證在更新以後將變量寫到其餘的線程可見的地方,個人理解是寫到主內存而不是每一個線程的工做內存。在讀取的時候也是從主內存而不是本身的私有拷貝中讀取值。可是不保證原子性,不保證線程安全,自增操做就是不安全的,由於自增操做依賴於當前的值,具備取值-改值-寫入的特性,改值和寫入是原子的,但取值不是原子的,在取值的過程當中可能被其餘線程改動。(可見性)
有序性:防止指令重排。例如在線程A中有一個初始化的操做,設置了一個flag;在沒初始化完成以前flag爲false,完成以後爲true;在另外一線程B中有個while循環,僅當flag設置爲true時才進行相關操做。若是給flag加了volatile,則flag不會進行重排操做,只有在進行完初始化操做才進行flag賦值爲true的操做。
每一個對象都有一個monitor鎖,加鎖的時候執行monitorenter
,釋放鎖的時候使用monitorexit
。嘗試獲取鎖的時候,過程以下:
若是monitor進入數爲0,則進入monitor並將進入數加1。
若是線程已經佔有了monitor,只是從新進入,則將monitor的進入數加1。
若是有其餘的線程佔有了monitor,則該線程進入阻塞狀態,直到monitor進入數爲0,從新嘗試獲取monitor。
方法的同步JVM底層常量池多了ACC_SYNCHRONIZED
,底層仍然是調用monitor實現的。
維持線程封閉的一種方法,每一個線程擁有一個屬於本身的副本,其get方法返回的值老是當前線程中調用其set方法設置的值。這些用於特定線程的值保存在Thread對象中 。使用的典型例子如多線程數據庫的Connection鏈接。
AQS維護了一個先進先出的雙向隊列,隊列元素Node節點爲其內部類;還維護了一個共享資源的狀態量,volatile int state,用來記錄共享資源的狀態。state有三個相關的訪問方法:
getState()
setState()
compareAndSetState()
AQS 定義了兩種資源訪問的方法Exclusive(獨佔方式如ReetrantLock)和 Share(共享方式,如Semaphere和CountDownLatch)。
若是某個同步器支持獨佔的獲取操做,則須要實現一些保護方法,tryAcquire、tryRelease 和 isHeldExclusively;而對於支持共享獲取的同步器,則應該實現tryAcquireShared、tryReleaseShared等方法。在AQS中的acquire、acquireShared、release、releaseShared方法均須要調用對應方法的try版原本判斷某個操做是否可以執行。
控制多線程狀況下某個資源能被訪問的次數。經過acquire()獲取一個許可,若是沒有就等待,經過release()釋放一個信號。若是設置信號量的個數爲1,則能夠看成互斥鎖使用
與Semaphore不一樣的是,CountDownLatch 的release操做(countDown)使得計數值減小,當計數值減小到0的時候,await操做才能獲取許可。至關於有多把鎖,這些鎖都被打開(countDown),才能將門打開(await)。latch.await()的對象都存儲在隊列中。
提供了一些原子的操做,例如CAS,incrementAndGet;CAS是指比較當前的變量值與指望的變量值,若是相等才進行賦值新值的操做。例如在incrementAndGet中就是在一個無限循環中不停的與指望值比較,相等了才Set新的值;有Bug,ABA問題,智能保證獲得的值與指望值相等,不能知道其在檢查的過程當中有沒有改變。J.U.C 提供了一個AtomicStampedReference來解決這個問題,如名稱,郵戳,經過控制變量版原本保證CAS的正確性。
配合Callable使用獲取多線程call方法返回的結果,當其調用get方法時,get將是阻塞的,直到callable線程返回告終果;在get方法以前能夠進行其它的動做。FutureTask實現了Runnable和Future兩個接口,能夠提交給ExecutorService,也能夠提交給Thread。
用於構造不可變對象,但在多線程的語義中,final能確保初始化過程的安全性,內存的可見性,從而能夠不受限制的訪問不可變對象,共享這些對象時無需同步。
Thread類提供了一個getStackTrace()方法也能夠用於獲取線程堆棧。
yield:Thread的方法,讓步,放棄CPU時間到等待運行狀態(而不是阻塞狀態),若是有優先級較高的線程則運行優先級高的線程,不然仍然運行被讓步的線程。
sleep:Thread方法,睡眠,放棄CPU時間到阻塞狀態,阻塞指定的時間,可是不放棄對象鎖,容許較低優先級的線程獲取機會。
join:Thread方法,加入,主線程要等待該線程執行完畢才能進行join()方法以後的調用
wait/notify/notifyAll:Object方法,等待/喚醒/喚醒全部;假如一個obj調用了這組方法,必須配合sychronized(obj)使用,在同步塊中。調用了這組方法,將釋放同步塊拿到的obj的鎖。調用wait()不加參數的方法將一直等待,直到收到了notify的信號才繼續執行。
將任務的執行和任務的提交解耦開來。避免爲一個任務開啓和銷燬線程所作的開銷。Executors
工廠類新建ExecutorService
對象,有兩種方式向該對象提交線程。
使用ExecutorService
接口中定義的方法submit
;該方法有幾種重載形式。
Future<T> submit(Callable<T> task)
: 該對象以獲得Future
類型的的返回值,使用future.get()
方法獲取。
Future<?> submit(Runnable task)
: 此時的返回對象仍然是Future
可是因爲Runnable
並無返回值,因此future.get()
方法獲取的返回值爲null
。
Future<T> submit(Runnable task, T result)
: 此時因爲Runnable
並無返回值,可使用一個對象result
將返回結果帶出來。
使用Executor
中定義的方法void execute(Runnable command)
,此方法沒法返回返回值。
ExecutorService
的默認實現類爲 ThreadPoolExecutor
,其構造方法爲:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime
TimeUnit unit, BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
各個參數的說明以下:
corePoolSize
: 線程池中工做線程的數量,當任務數超過這個數量,新的任務將被放到阻塞隊列中。
maximumPoolSize
:線程池中最大線程的數量,當任務隊列滿了,並且運行任務的數量小於maximumPoolSize
,仍然建立新的線程處理任務。若是隊列已滿且線程數量也達到了maximumPoolSize
,若是使用的阻塞隊列是ArrayBlockQueue<Runnable>
,則拋出異常RejectExecutionException
;若是使用的阻塞隊列是LinkedBlockQueue<Runnable>
,由於對大小沒有限制,因此不存在上述問題。
keepAliveTime
:指超過corePoolSize
的線程在不工做狀況下的生存時間,若是一個線程空閒的時間超過keepAliveTime
,則會終止,直到線程池中線程的數量減小到corePoolSize
。
unit
:參數keepAliveTime
的單位。
workQueue
:阻塞隊列
handler
:表示當拒絕處理任務時的策略。有丟棄任務拋出異常/不拋出異常/丟棄隊列的任務嘗試執行任務/由調用線程處理該任務。
還有一個構造函數其中有個參數threadFactory
用來建立線程。
建立一個可緩存線程池,能夠靈活的建立和回收線程,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
建立固定長度的線程池;
建立一個定長線程池且支持定時或週期性的執行任務;
建立一個單線程的線程池。
若是隊列滿了,並且此時運行的數量小於maximumPoolSize,則仍然建立新的線程運行任務。(通常狀況下運行的線程數爲corePoolSize)。若是隊列已滿並且線程數也到達maximumPoolSize,則拋出異常(RejectedExecutionException)。
以上狀況針對於ArrayBlockingQueue<Runnable>
,若是是LinkedBlockingQueue<Runnable>
,由於該隊列無大小限制,因此不存在上述問題。
對於密集型計算任務,在擁有 個處理器的系統上,當線程池的大小爲 時,一般能達到最優的利用率。
能夠根據任務等待時間與服務時間的比值來計算線程池的大小。能夠定義以下的公式:
其中,表明cpu的數量, 指cpu的平均利用率, 指等待時間與服務時間的比值。從公式中能夠看出,cpu密集型任務(較小)所需設置的CPU數量較小,I/O密集型任務(較大)所須要的CPU數量較大。
ReentrantLock:可重入鎖,功能與sychronized至關(sychronized)也是可重入的,必需要在finally塊中釋放鎖,所以sychronized較爲安全,並且sychronized是基於JVM的,lock是基於JDK的。相比於sychronized,實現了幾個高級功能
等待可中斷:當持有線程請求的鎖的線程長時間不釋放鎖時,能夠選擇放棄等待鎖。tryLock(long timeout, Unit unit)
。
公平鎖:sychronized和ReentrantLock在默認狀況下實現的都是非公平鎖,即:當一個線程請求鎖時,這個鎖正好是可用的,沒必要理會其它請求該鎖的線程而直接佔用該鎖,使用new RenentrantLock(boolean fair)來建立公平鎖。雖說公平,可是公平鎖的吞吐量較低,由於每次鎖釋放時,將隊列頭的線程喚醒到就緒狀態是比較消耗資源的。非公平鎖的插隊搶佔方式帶來了吞吐量的提高
ReentrantReadWriteLock:讀寫鎖,在基於AQS實現的ReetrantReadWriteLock中,維持了兩個狀態量,一個寫入鎖的狀態量,一個讀取鎖的狀態量。在讀取鎖上的操做將使用共享的方法獲取鎖,在寫入鎖上的操做將使用獨佔的獲取方法與釋放方法。AQS在內部維護一個等待線程隊列,其中記錄了某個線程請求的是共享訪問仍是獨佔訪問。在ReentrantReadWriteLock中,當鎖可用時,若是位於隊列頭的是寫線程,則獨佔鎖進行寫入。當位於隊列頭的是讀線程,則在隊列中第一個寫入線程以前的多有讀線程都將得到這個鎖。
利用共享對象共享數據,wait/notify/notifyAll
、await/signal/signalAll
進行喚起和等待。若是兩個線程的動做相同,能夠在線程類內部建立該共享數據,若是兩個線程動做不一樣,能夠將共享數據另外封裝成一個對象,而且對該對象進行共享操做。
主要要考慮到鎖住一個對象以後請求另外一個對象的鎖,而這個對象被另外一個線程鎖了。
public class DeadLock{
private boolean switch ;
private Object obj1 = new Object();
private Object obj2 = new Object();
public DeadLock(boolean switch){
this.switch = switch ;
}
死鎖測試方法以下
public static void main(String[] args){
DeadLock lock1 = new DeadLock(true) ;
DeadLock lock2 = new DeadLock(false) ;
}
異常(Throwable)分爲Error和Exception;Error分爲VirtulMachineError和AWTError;VirtulMachineError包含常見的棧溢出(StackOverFlow)異常和內存溢出(OutOfMemoryError)異常;Exception包含IO異常(IOException)和運行時異常(RunTimeException)。運行時異常不捕捉也能夠,Error不須要被捕捉。 也就是說,必需要被捕捉的異常只有IO異常?
Iterable是一個接口,其中有一個方法Iterator<T> iterator();
返回一個Iterator對象。Iterator對象包含相關的迭代器方法,next()
,hasNext()
, remove()
方法。List等集合函數實現了Iterable接口;在具體的實現類中,iterator()
返回了一個繼承Iterator的內部類Itr
。Iterator
在java.util包下面;而Iterable
在java.lang包下
Comparable在java.lang包下;Comparator在java.util包下;Comparable接口只有一個實現方法,compareTo(T o),其中o是要比較的元素;而Comparator做爲比較器,jdk1.8實現了不少默認方法(default method),繼承它的實現類須要要實現compare(T o1, T o2)方法。Comparator是一個外部比較器,即單獨定義一個比較器來實現對象的比較,而Comparable是一個內部比較器,經過對象調用自身的compareTo方法來實現與其餘類的比較。若是但願實現比較器進行在有序集合中的排序,實現comparable較爲簡單,可是須要改動原有類;comparator做爲外部比較器不須要改動原有類,但須要同時將比較器做爲參數傳入相關的方法或通知相關的類。例如Collections.sort(arrayList, myComparator)
;
String 能夠被繼承碼?不能夠,String 被聲明爲final類。
重載的函數參數類型必定不一樣:1)數量不一樣;2)類型(順序)不一樣。
在編譯階段進行了泛型擦除,虛擬機是看不到泛型的,擦除的類型將被單獨記錄在Class文件中的Attributes域內,而在使用泛型處作類型轉換和類型檢查。擦除規則叫作保留上界。
<T>
擦除後變爲Object;
<? extends A>
擦除後變爲A;
<? super A>
擦除後變爲Object;
static方法是類方法,因此sychronized修飾類方法拿到的鎖是Class的鎖;而普通方法拿到的鎖是當前對象的鎖;兩個鎖並不會產生衝突;若是要在類方法和普通方法裏實現同步,能夠在方法體內使用sychronized同步塊對一個鎖對象加鎖。
怎麼樣實現一個不可變類?
將類聲明爲 final。
將屬性聲明爲 private且不提供 setter 方法。
全部成員變量聲明爲final,在構造方法中賦值。
實現深拷貝,getter 方法返回對象的深拷貝。
深拷貝和淺拷貝
Object類的clone方法是一個淺拷貝。等於號是一個地址拷貝,指向的是同一個對象;clone()方法生成了一個新的對象,對象中的基本類型是直接拷貝過來的,可是對象中的引用類型指向的仍然是原對象的數據。Collections.copy()方法是一個深拷貝。
二叉樹前中後遍歷,知道某兩個推出第三個,哪些能推出,哪些推不出?
前序和後續,沒有中序的推不出,由於只能肯定父子關係,不能肯定左右樹的關係。
http:創建鏈接(DNS)--> 發送請求 --> 響應請求 --> 關閉鏈接
https:請求創建鏈接 --> 服務端發送證書(包括公鑰)給客戶端 --> 協商加密等級 --> 客戶端根據加密等級將會話密鑰利用公鑰進行加密--> 服務端利用私鑰解密會話密鑰並利用會話密鑰加密通訊的信息。
在HTTP/1.0中,默認使用短連接。每進行一次HTTP傳輸,就創建一個HTTP鏈接,當任務結束,該鏈接就斷開。從HTTP/1.1起,改用默認長鏈接,用以保持鏈接特性。使用長鏈接的協議,會在響應頭有Connection:keep-alive
代碼。在使用長鏈接時,客戶端訪問一個網頁完成後,客戶端和服務器之間用於傳輸的TCP鏈接不會關閉,當客戶端再次訪問這個網站上的網頁時,會複用這條已經創建的鏈接。Keep-Alive
不會永久保持,能夠在不一樣的服務器軟件設定這個時間。
對比 :短連接的優勢是控制起來比較簡單,保存的鏈接都是有效的鏈接;可是因爲每次創建鏈接銷燬鏈接須要消耗資源,效率較低。
Http/1.1默認支持長鏈接
節約帶寬:HTTP/1.1 支持只發送Header信息(不帶body信息),若是服務器認爲客戶端有權限訪問則返回100,此時再發送body信息;若是服務器返回401,則無權限,沒必要再發送Body信息,節約了帶寬。
多路複用:HTTP2.0使用了多路複用技術,作到統一個鏈接處理多個數據請求,並且併發的數據量比HTTP1.1大了好幾個數量級。
數據壓縮:HTTP2.0支持HPACK算法對header的數據進行壓縮。
服務器推送:對支持HTTP/2.0的server進行請求數據的時候,服務器會把一些客戶端須要的資源一塊兒推送到客戶端,以避免客戶端再次請求創建鏈接。很是適合加載靜態資源。推送的這些資源其實是存放在客戶端的=某處,並不須要佔用帶寬。
301和302的區別:301是永久重定向HTTP,搜索引擎會抓取新的網址內容同時將舊的網址替換爲新的網址;302是暫時重定向,搜索引擎會抓取新的內容而保留舊的網址,可能會發生網址劫持問題。例如好的URL重定向爲壞的URL,但網址的內容是存在於壞的URL中的,搜索引擎所顯示的url結果倒是好的URL,這時發生了對壞URL網址的內容劫持。
鏈路層:典型協議ARP(地址解析協議 IP->MAC)和RARP (逆向地址解析協議)
網絡層:IP協議,ICMP(Internet 控制消息協議,容許目標主機或者路由器給予數據發送方提供反饋信息,ICMP是IP協議的一部分,任何實現了IP協議的設備同時也要使用ICMP協議),和IGMP協議(Internet工做組管理協議),用於廣播流量的管理,只廣播給一部分主機,組播。
傳輸層:TCP和UDP協議
應用層:電子郵件,超文本傳輸等等協議
1.物理層;2.數據鏈路層;3.網絡層;4.傳輸層;5.會話層;6.表示層;7.應用層
三次握手:請求連接的客戶端向主機發送syn信號和sequence number = x 並進入SYN_SENT狀態;服務器收到syn信號必須確認syn信號,將ack信號設置爲x+1,而且本身也將發送一個syn信號和自身的seq number = y,服務端進入SYN_RECV狀態;客戶機收到服務端發送的SYN+ACK信號,向服務器發送確認信號 ACK = y+1,發送完畢後,服務機和客戶機都進入ESTABLISHED狀態,完成三次握手。使用三次握手是爲了防止發生服務器忙等狀態。當客戶機發送SYN信號後,因爲各類緣由在網絡中延遲了,此時客戶機超時重發SYN信號而且成功創建了連接併發送了數據;而在數據發送完成後,以前沒有到達的SYN到達服務機,服務機產生應答並等待客戶機發送信息,而此時客戶機並無要發送信息,形成服務機的空等待。
四次揮手:主動關閉方發送FIN信號,進入FIN_WAIT_1狀態;被動關閉方接收FIN信號,併發送ACK確認信號,進入CLOSE_WAIT狀態,此時被動關閉方仍然能夠向主動方發送數據;主動方收到FIN信號後進入FIN_WAIT_2狀態,當被動方數據發送完畢,向主動方發送FIN信號,被動方進入LAST_ACK狀態;主動方接收到FIN信號,並向被動方發送一個ACK確認信號,進入TIME_WAIT狀態,若是等待2MSL服務器沒有想應信息,則說明關閉成功,進入最終的CLOSE狀態。至此,四次揮手執行完畢。
滑動窗口:因爲TCP鏈接在傳輸信息時須要收到返回確認信息才能進行接下來的傳輸,這種協議浪費了大量的寶貴帶寬。TCP協議經過滑動窗口技術來提升網絡的吞吐量。具體作法是在要發送的數據上放置一個滑動窗口,發送時將窗口中的全部分組都進行發送,當收到第一個分組的確認信息後,能夠將窗口向後移動一個分組。隨着確認的送達,窗口持續向後移動。
典型基於此的應用層協議HTTP, SMTP, POP3,FTP
將數據截斷爲合理的長度
超時重發
對於收到的請求,給予確認響應
校驗出錯將丟棄不予以響應
對失序數據將進行重排序,而後才交給應用層
發送方維持一個擁塞窗口(cwnd) ,本身的發送窗口等於或小於擁塞窗口。
主機開始工做時,若是當即把大量數據發送到網絡,有可能引發網絡擁塞。較好的方法是先試探一下,由小到大的增長窗口。先將窗口設置爲一個初始大小(通常設置爲一個最大報文段的大小)。每當收到一個對新的報文段的確認以後,就將擁塞窗口加倍,這個過程叫作慢開始 。
爲了防止擁塞窗口一直增大引發網絡擁塞,須要設置一個慢開始門限,當擁塞窗口的大小大於這個門限的時候,進行擁塞避免 的操做,擁塞避免的思路是讓擁塞窗口緩慢的增大,將以前的指數型增長改成線性增長。
不管是在慢開始階段仍是在擁塞避免階段,每當發送方判斷到網絡出現了擁塞(根據是沒有按時收到確認),就把慢開始門限減少到原來的一半,而後吧慢開始的擁塞窗口從新設置爲1並開始慢開始算法。
快重傳 算法要求接收方每收到一個失序的報文段就對發送發發送重複確認,而不要等待本身發送數據時才進行捎帶確認。例如,接收方接收到了M一、M2和M4,M4接收到以後由於是失序的,因此再次進行對M2的確認,以後若是一直沒收到M3,在接收到M5,M6的時候仍然須要對M2進行確認。當發送方接受到3個連續的重複確認以後,就應當當即重傳對方還沒有接收到的報文M3,而沒必要等到爲M3設置的重傳計時器到期。儘早重傳報文段可使網絡吞吐量增長。與快重傳同時進行的還有快恢復 算法,一樣把慢開始門限減半,可是擁塞窗口不是從1開始,而是設置爲慢開始門限減半後的值,同時執行擁塞避免算法(加法增大)。
盡最大能力交付,典型的基於此的應用層協議例如DNS協議和TFTP
session:Session是另外一種記錄客戶端狀態的機制,不一樣的是,session是保存在服務端的。
cookie:是存儲在客戶端的一小段信息。客戶端請求服務器,若是服務器須要記錄用戶信息,就使用response向客戶端瀏覽器頒發一個cookie。客戶端瀏覽器會把cookie保存起來。當瀏覽器再次請求網站時,瀏覽器把請求連同cookie信息一塊兒發送給服務器。服務器檢查cookie信息,以此來判斷用戶狀態。Cookie並不提供修改、刪除操做。若是要修改某個Cookie,只須要新建一個同名的Cookie,添加到response中覆蓋原來的Cookie。刪除cookie時經過setMaxAge
來刪除。
分爲高級調度(做業調度)、低級調度(進程調度)、中級調度(中程調度)。高級調度就是指對外存上做業的調度,低級調度是指內存中對排隊線程的調度,中程調度是指爲了提升內存利用率把暫時不能運行的進程再也不佔用寶貴的內存資源而將他們調到外存上等待,這種狀態叫作掛起狀態。
先來先服務算法(FCFS):有利於長做業而不利於短做業。
短做業優先算法(SJF):每次都優先選擇所用時間較短的做業進行服務。
高優先權優先調度算法
分類:非搶佔式和搶佔式
優先權類型:靜態優先權和動態優先權
高響應比優先調度算法:優先權 = (等待時間+要求服務時間) / 要求服務時間
基於時間片的輪轉調度算法
基於存儲器系統共享
共享數據結構,例如生產者-消費者問題中的共享緩衝區,這種模式下,公共緩衝區的設置以及進程間的同步設置都是程序員的職責。只適用於傳遞叫少許的數據。
共享存儲區,在存儲區開闢公共空間,適合傳遞大量的數據。
管道通訊
管道是指連接一個讀進程和一個寫進程之間的共享文件,pipe文件。適合大量數據的傳輸。
信號量(Semaphore):是一個計數器,用來控制多個進程對共享資源的訪問。不是用來交換大批數據,而是經常使用於多線程之間的同步。
消息隊列:消息隊列是消息的鏈表,存放在內核中並由消息隊列標識符標識。克服了信號量傳遞信息少,管道只能承載無格式字節流以及緩衝區大小受限等問題。
信號機制(signal):信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。
套接字(socket):可用於不一樣機器間的進程通訊。
競爭資源
進程推動順序非法
互斥條件
請求和保持條件
不剝奪條件
環路等待條件
預防是指破壞死鎖必要條件的幾個條件,以此來預防死鎖的發生。(預:以前防止),主要的方法有:
破壞請求和保持條件:這種方法規定在進程分配資源時一次性分配給進程
摒棄不剝奪條件:當進程的資源不能當即獲得知足時,必須釋放本身請求的資源
摒棄環路等待條件:全部進程對資源的請求必須按照對進程編號的遞增順序提出
死鎖避免算法:銀行家算法,在檢測到系統不會進入不安全狀態纔會對進程進行資源的分配
將用戶進程分配在連續的連續的內存空降,這樣會產生較多的碎片。
將存儲空間劃分爲大小必定的頁面。
頁表:存儲了頁號和物理塊號的關係。
尋址時(邏輯地址轉換爲物理地址)時,有一個頁表寄存器記錄了頁表起始地址和頁表長度,邏輯地址包含頁號和業內地址,首先將頁表始址與頁號和頁表長度的乘積相加,獲得該表項在頁表中的位置,由此能夠獲得該頁的物理塊號,將至裝入物理地址寄存器中,與此同時將有效地址寄存器中的頁內地址送入塊內地址字段中,計算獲得物理地址。
有一個記錄了當前訪問的頁表項的高速寄存器,從寄存器尋找頁號對應的物理地址並與頁內地址拼接生成物理地址。
頁式存儲爲了提升內存的利用率,段式存儲爲了知足程序員在編程和使用上多方面的需求。例如,方便編程(按邏輯劃分爲若干段),信息共享,信息保護,動態鏈接(運行時須要該段才調入內存)等。
段表:每一個段在表中佔有了一個表項,其中記錄了該段在物理地址中的起始地址(基址)和段的長度。
尋址:根據段表始址和該段的短號計算出該段對應的段表項的位置,從中讀出該段在內存中的起始地址,將該段的起始地址與段內地址相加獲得物理地址。
頁式物理單位,分頁減小內存碎片;段式信息的邏輯單位,其含有一組意義相對完整的信息;爲了能更好的知足用戶的須要。
段式系統有一個突出優勢,是易於實現信息的共享(將共享的部分劃分爲一個段便可),容許若干個進程共享一個或多個分段,且對段的保護也簡單易行。
既可以便於實現、分段可共享、易於保護、可動態連接,有可以減小內存碎片。
最佳置換算法:每次置換出來的都是其後再也不使用或者最長時間再也不使用的塊。(不可實現)
先進先出置換算法。
最近最久未使用(LRU)。(特殊的棧支持)
Clock置換算法:僅須要爲每一頁設置一個訪問位,再將內存中的全部頁面都經過鏈指針鏈接成爲一個循環隊列,某一頁被訪問時,將標誌位置爲1。置換算法在選擇某一頁進行置換的時候,只需檢查訪問位,若是是0,則選擇該位進行換出,若爲1,則將1置爲0且不換出,再按先進先出算法檢查下一個頁面。
強調原子性,便是,列是不可再分的。考慮數據表列中含有一個列是用戶電話,而電話包含工做電話和私人電話,這樣的數據表就是不符合第一範式的。
首先是第一範式,而後知足兩個條件,其一必須有主鍵,其二表中的非主鍵列必須徹底依賴於主鍵,而不能只依賴於主鍵的一部分。例如考慮到一個訂單明細表含有【訂單號,商品號 ,商品名稱,商品價格】,其中訂單號和商品號是複合主鍵,可是商品名稱和商品價格只依賴於商品號,所以部分依賴於主鍵,這樣也是容易產生冗餘,例如不一樣的訂單裏含有相同的商品,商品名稱和商品價格會產生重複(冗餘),這樣部分依賴於主鍵的表設計不符合第二範式。
首先是第二範式,其次不能出現傳值依賴。傳值依賴是指,例如一張訂單表【訂單號,訂單客戶ID,訂單時間,客戶姓名】,其中訂單表是主鍵,訂單時間,訂單客戶ID徹底依賴於訂單號,可是客戶姓名卻依賴於訂單客戶ID(非主鍵),因此這個表不符合第三範式。存在着冗餘。
B+樹爲了存儲索引,索引通常數據量比較大,存儲在磁盤上。一次讀取磁盤I/O的時間比較大,因此要求I/O次數要小。B-樹因爲中間節點存儲了數據,每一個節點數據量就比較大,而每次I/O讀出數據是必定的,因此B-樹的讀取次數就比較多。而B+樹中間節點是不存儲數據的,所以I/O次數較少。同時因爲只在葉子節點存儲數據,樹高不會有太大差異,因此每次查找效率至關,查找比較穩定。
悲觀鎖:悲觀的認爲業務操做是可能失敗的,要先獲取鎖,再進行業務操做。「一鎖二查三更新」即指使用悲觀鎖。一般來講,數據庫一般使用select ... for update
來實現悲觀鎖,使用該語句時,會獲取該語句查詢到的行的行鎖,其餘併發執行的select for update將會等待其釋放鎖。須要注意的是,mysql行級鎖都是基於索引的,若是一條sql語句查詢的條件不是索引,mysql會鎖整張表,形成效率問題 。適用於事務操做頻繁易於產生數據衝突的狀況。
樂觀鎖:在提交數據更新以前檢查是否發生了衝突,發生了則進行事務的回滾。使用版本還進行實現。適用於不易產生數據衝突的狀況。
未提交讀(Read Uncommitted):在事務沒有提交時就容許讀取數據。容許其餘事務讀取沒有提交的數據。髒讀:一個事務讀取到了另一個事務沒有提交的數據。重點在於事務過程當中讀取。
提交讀(Read Committed):只能讀取已經提交的數據。解決了髒讀問題。Oracle等多數數據庫默認都是該級別 。但有一個問題:不可重複讀 。在一個事務裏,有屢次讀取操做,可是在兩次讀取之間有其餘的事務改變了數據,此時致使兩次讀取的數據不一樣,讀取數據的時候其餘的事務不能對數據進行修改 。重點在於事務過程當中修改 。
可重複讀(Repeatable Read):可重複讀,在同一事務內,查詢的數據始終與事務開始時是一致的。InnoDB的默認級別,消除了不可重複讀,可是存在幻讀現象。幻讀是指一個事務對錶進行操做,改動涉及了表中的全部數據。同時第二個事務向表中插入一行新的數據,此時操做第一個數據的用戶就會發現數據表中還有沒有修改的數據,好像產生了幻覺同樣。重點在於事務過程當中增長或刪除 。
可串行化(Serializable):徹底串行化的讀,每次讀都要得到表級別的鎖,讀寫都會相互阻塞。
A(Atomicity 原子性):一個事務要麼執行,要麼不執行。
C(Consistency 一致性):事務的運行不改變數據的一致性,例如,完整性約束了a+b=10,事務改變了a,b也隨之改變。
I(Isolation獨立性):事務之間不會交替執行。
D(Durability持久性):改變的數據持久的留在數據庫中。
InnoDB的 MVCC主要是爲Repeatable-Read(可重複讀)事務隔離級別所作的。在InnoDB的MVCC中,在每一行後面隱藏着兩個列,一個保存了行的建立時間(版本號),一個保存行的過時時間(或刪除時間)。這裏的時間並非指的實際的時間,而是系統的版本號。沒開始一個新的事務,系統的版本號都會自動遞增。在Repeatable read隔離級別下,MVCC具體的操做實現以下:
SELECT
: 會根據下面的兩個條件查詢每行記錄:
InnoDB只查找建立時間小於等於當前版事務本號的的數據行,能夠確保當前的事務讀取的行是事務開始以前就存在或者由該事物建立。
InnoDB只會查詢刪除版本不存在或者版本號小於當前事務版本號的數據,能夠確保查詢的數據在當前事務開始以前沒有被刪除。
INSERT
: InnoDB爲新插入的每一行做爲當前的版本號做爲行版本號。
DELETE
: InnoDB爲刪除的每一行保存當前的系統版本號做爲行刪除標誌版本號。
UPDATE
: InnoDB將爲更新的行保存當前的版本號做爲建立版本號,複製出該行進行修改;同時將更新以前的行的刪除版本號設置爲當前版本號;複製的行修改後的版本號等於以前的版本號才進行事務的提交,不然進行回滾。
支持了ACID事務,提供了數據庫的四種完整性約束,提供了行級鎖和完整性約束,MySQL在運行InnoDB的引擎時,會在內存中創建緩衝池,用於緩衝數據和索引,其設計目標是處理較大數量的數據。當要使用數據庫事務時,該引擎時首選,由於支持更小粒度的鎖,寫操做不會鎖整張表,適合併發較高時提高效率。可是該引擎沒有保存行數,select count(*)from table
須要掃面整張表。
其索引結構爲B+樹,索引文件自己就是數據文件,即B+樹數據域存儲的自己就是data數據,索引的key就是數據表的主鍵,所以InnoDB數據表自己就是主索引 。一個主索引的例子如圖所示:
對於輔助索引 來講,B+樹數據域存儲的是主鍵的值,以下圖所示
這種基於主鍵的彙集索引使得按主鍵查詢效率較高,可是對於輔助索引來講,須要查詢兩次,首先須要查詢到主鍵,而後經過主鍵查詢數據。這種在索引中直接存儲數據的索引方式成爲聚簇索引 。
沒有提供對數據庫事務的支持,也不支持行級鎖和外鍵。每次對錶進行事務操做時須要鎖整張表,效率較低。但存儲了數據記錄的行數。若是數據庫的讀操做遠遠多於寫操做且不須要數據庫事務的支持,那麼MyISAM也是一種好的選擇。
其索引結構一樣是B+樹,但不一樣於InnoDB的是,B+樹中存儲的是實際數據的地址,地址指向了實際數據。這種索引的形式叫作非聚簇索引 如下兩張圖分別給出了主索引 和 輔助索引 的例子。
大尺寸的數據趨向於選擇InnoDB,由於其支持事務處理和故障恢復。數據庫的大小決定了恢復的長短,InnoDB能夠利用事務日誌進行數據恢復,這會比較快。主鍵查詢在InnoDB也會至關快。大批的INSERT語句(在每一個INSERT語句中寫入多行,批量插入)在MyISAM下會快一些,可是UPDATE語句在InnoDB下則會更快一些,尤爲是在併發量大的時候。
瞭解數據庫底層原理對咱們正確使用和優化數據庫都有好處,好比:
因爲InnoDB直接存儲主鍵數據做爲索引,因此不宜用過長的主鍵來做爲主鍵,由於這會增長索引的數據大小,過長的主索引大小會令輔助索引變得過大。
使用非單調的字段做爲主鍵不是一個好主意,這可能會使索引爲了維持B+樹的特性而不停的分裂調整,十分低效。推薦使用遞增的數據列做爲主鍵。
B+樹
散列索引:Hash的形式,將散列值相同的數據放到同一個桶中。
位圖索引:
普通索引、惟一索引、主鍵索引、組合索引。
Inner join、left join、right join、outer join、cross join
類A使用到了類B,但這種關係具備偶然性、臨時性,非必然。具體表現爲A中的某個方法使用到了B。
是指兩個類或者類與接口之間比較強的依賴關係,關係不是偶然的,是長期,穩定的,雙方的關係也是平等的。表如今代碼裏,就是被關聯的B以屬性的方式出如今A中。
是關聯關係的一種特例,其表現的是一種總體與部分的關係、用有的關係,此時總體和部分是能夠分離的,他們有各自的生命週期。好比公司與員工,電腦與CPU的關係。表如今代碼層面,與關聯關係是同樣的,只能經過語義來判斷。
組合也是關聯關係的一種特例,這種關係比聚合強,也稱爲強聚合關係;也體現的是總體與部分的關係,此時總體與部分是不可分的,好比人和大腦的關係。代碼層面上,表現與關聯關係也是一致的,只能經過語義判斷。
關係的強弱關係通常來講組合>聚合>關聯>依賴。
懶漢模式
public class SingletonInstance{
private SingletonInstance instance ;
private SingletonInstance(){};
public static SingletonInstance getInstance(){
if(instance == null)
instance = new SingletonInstance();
return instance ;
}
}
這種狀況下使用了lazy loading,可是是線程不安全的,能夠對getInstance方法進行sychronized關鍵字包裝實現同步,可是這樣就損失了效率
餓漢模式
public class SingletonInstance{
private static SingletonInatance instance = new SingletonInstance();
private SingletonInstance();
public static SingletonInstance getInstance(){
return instance;
}
}
這種狀況下基於classLoader方式規避了線程安全問題,可是類的裝載有不少種可能,不能保證必定是調用getInstance致使了類的裝載,因此沒有達到lazy loading的效果,可能浪費資源。
內部類方式
public class SingletonInstance{
private static class SingletonHolder{
private static final SingletonInstance INSTANCE = new SingletonInstance();
}
private SingletonInstance(){};
public static getInstance(){
return SingletonHolder.INSTANCE ;
}
}
這種方式只在調用getInstance的時候才進行對SingletonInstance的實例化,比餓漢模式更加合理。
用戶在訪問網頁時,CDN選擇一個離用戶最近的CDN邊緣節點來相應用戶請求。
好處:
用戶:距離較近的節點相應請求,加快了對請求的響應速度
服務器:因爲處理請求在邊緣節點,起到了分流做用,減輕了源服務器的負載
爲何要NoSql?
高併發讀寫問題
海量數據問題,查詢效率
高可用性和高擴展性,不能經過添加服務器和節點來解決問題。
緩存
網站訪問統計
任務隊列
分佈式構架中的session分離 :在分佈式構架中,在一臺服務器上保存了session,可是有可能該用戶的另外一請求被負載均衡到另外一個服務器,此時就須要對session進行分離,將session寫在Redis等非關係型數據庫中,而且有相關的備份。讀取都從Redis中讀取,寫入也都寫入到Redis服務器。可容錯且實時性較強。
字符串:get、set、incr(增長操做)、decr(減少操做)、incrby num 3
(給num加3)、decrby num 3
、append num 5
、
Hash:hset myhash username jack
; hmset myhash usename rose age 2
;hget myhash user name
; hmget myhash usename age
; hgetall myhash
; hexists myhash username
; hkeys myhash
; hvals myhash
;
List: 按照插入順序排序的字符串鏈表。能夠在頭部、尾部插入元素。
lpush mylist a b c
: 左側插入
rpush mylist a b c
: 右側插入
lrange mylist 0 5
: 顯示0到5位置的元素
lpop
、 rpop
: 彈出元素
lrem mylist 2 3
: 從前日後刪除2個3
lrem mylist -2 1
: 從後往前刪除2個1
lrem mylist 0 2
: 刪除全部的2
lset mylist 3 mmm
: 設置index爲3的元素爲mmm
linsert mylist before/after b 11
: 在b元素以前/以後插入11
rpoplpush mylist1 mylist2
: 從mylist1右側彈出數據插入到mylist2的左側
Set: 不容許重複元素,容許集合操做
sadd/srem...
smembers myset
: 展現元素
sismember myset a
: 判斷a是否在myset中
sdiff mylist1 mylist2
: mylist1 和 mylist2 的差集 ;
sinter mylist1 mylist2
: 並集
sunion mylist1 mylist2
: 交集
scard
: 返回成員數量
srandmember myset
: 隨機返回成員
sdiffstore myset1 myset2 myset
: 將myset1和myset2的差集存到myset中,其餘集合操做類似。
Sorted-Set : 每一個成員都有一個分數,Redis根據其分數進行排序。
zadd mysort 70 zhansan 80 lisi 90 wangwu
: 添加元素和其對應的分數
zscore mysort zhangsan
: 獲取分數
。。。
支持多數據庫,16個數據庫,編號從0到1;
multi //開啓事務
exec //提交事務
discard //回滾事務
RDB:默認支持,指的是在制定的時間間隔內將數據寫入到磁盤。
全部的數據庫都只包含一個數據文件,方便文件的恢復。效率較高。
不能最大限度的保持數據的可用性,沒來得及往硬盤上寫可能發生宕機發生數據的丟失。數據集較大可能會使服務器中止實現備份。
save 300 10
: 每300秒有10個key發生變化會向硬盤上寫一次
AOF:將以日誌的形式記錄Redis服務器執行的每個操做。
能夠帶來更高的數據安全性。以append方式持久化,不會丟失以前的數據文件。有日誌文件記錄修改操做,能夠經過此文件完成數據重建。
持久化文件大,運行效率低於RDB。
appendonly yes
;
appendfsync always
: 發生操做就持久化;
appendfsync everysec
: 每秒同步;
appendfsync no
: 不一樣步。
跳錶是一種隨機的數據結構,將數據分層,每次提取一些數據節點,有如下的特色:
有不少層組成
每一層都是一個有序的節點
最底層包含全部的元素
若是一個元素出如今某一層,在其下方層中也將出現
每一個節點包含兩個指針,一個指向當前層的下一元素,一個指向下一層的相同元素。
搜索比較容易,從上往下開始搜索。
跳錶的插入,是先隨機一個K,K表明要將元素插入的層數,而後再level1 到 level K都插入合適的位置。
分佈式鎖:主要使用到了Redis中的setNX
命令,意思是set if not exist,即在不存在此key值的時候才能設置該key的value。客戶端可使用setNX foo.lock <current unix time>
來嘗試獲取鎖,當鎖已經被別人獲取時,setNX
命令將返回0,即不能爲該key設置value,反之能夠獲取該鎖。操做完成後使用Del foo.lock
釋放鎖。有可能一個任務獲取了鎖可是由於遇到了異常沒有釋放,此時可能發生死鎖問題。解決的方法是爲鎖加上一個過時時間,當一個線程嘗試獲取鎖失敗的時候用當前的時間減去鎖以前的value值(value值設爲加鎖時的時間);使用GET
方法過去鎖的時間戳,與當前時間對比, 若是超過了過時時間,能夠嘗試使用GETSET
命令獲取鎖,同時返回原鎖的時間戳,若是此時獲取的時間戳與以前GET
獲取的時間戳不一樣,說明在此過程當中有其餘的任務獲取了鎖,此任務仍然不能獲取鎖。
分佈式任務隊列:使用Sorted-set存儲任務,任務ID做爲value,當前時間做爲score,在入隊出隊時須要調用分佈式鎖。
見下一章
慢查詢日誌,尋找哪些查詢是有效率問題的,MySql提供了一個日誌工具,慢查詢日誌來記錄哪些數據庫查詢是有效率問題的。能夠設置查詢語句的時間超過某一特定值就記錄到慢查詢日誌中去,以此來分析性能瓶頸。
慢查詢日誌的的工具
mysqldumpslow 工具
pt-query-digest 工具
explain 查看數據庫的索引信息。explain select * from tb_***顯示的字段有
id:select 查詢的序列號
select_type:select查詢的類型,通常包含SIMPLE、聯合查詢、子查詢等
table:輸出結果所引用的表
type:聯合查詢所使用的類型
possible key:指出MySQL使用哪一個索引在該表中找到行。若是是空,表明沒有相應的索引
key:mysql決定使用的索引
....
Sql 語句優化
哪些列創建索引,索引的規則
在where從句,group by從句,order by 從句, on從句出現的列,鏈接的列。
max 函數所使用的列
索引字段越小越好,因爲數據庫分頁存儲,減小I/O次數,太大的字段不宜過多的創建索引。
離散程度大的列放到聯合索引前面,離散度是指列的不一樣的程度。
索引優化
索引會增長數據庫的寫入速度;並且過多的索引會使得數據庫在查詢時要找到須要使用的索引,這個過程也會下降查詢的效率。
刪除重複的索引:例如主鍵和在主鍵上創建的惟一索引就是重複的,由於InnoDB中主鍵自己就是一個索引了。
刪除冗餘的索引:例如在創建聯合索引的時候,人爲的加上了主鍵這一列,在InnoDB中,全部的索引都是有主鍵這一列的。
查找重複及冗餘索引的工具:pt-duplicate-key-checker
pt-duplicate-key-checker -uroot -p '' -h 127.0.0.1