一次電話Java面試的問題總結(JDK8新特性、哈希沖突、HashMap原理、線程安全、Linux查詢命令、Hadoop節點)


面試涉及問題含有:

Java

  JDK8新特性

  集合(哈希沖突、HashMap的原理、自動排序的集合TreeSet)

  多線程安全問題

  String和StringBuffer

JVM

  原理、運行流程、內部結構

Linux

  查詢含有某字符串內容的命令grep

  查詢進程、GC狀態、殺死進程

Hadoop五種節點介紹

--------------------------------------------------------------------------------------------------------

 

JAVA:

1、JDK8新特性:

  • 函數接口Functional Interface

    只包含一個抽象方法的接口,也成為SAM(Single Abstract Method單方法接口)類型的接口。

     例如Runnable接口的run()方法。JDK8新增了許多函數接口,其中一個重要的原因就是支持Lambda表達式。

  • Lambda(拉姆達) 表達式(閉包) − Lambda允許把函數作為一個方法的參數,函數作為參數傳遞進方法中。

      一種匿名方法,可以將函數接口中的函數作為方法參數處理。在Java中Lambda表達式返回值是一個對象,這個對象必須是單方法接口

      lambda表達式的重要特征:

        可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。

        可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。

        可選的大括號:如果主體包含了一個語句,就不需要使用大括號。

        可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

      Lambda的設計可以實現簡潔而緊湊的語言結構。最簡單的Lambda表達式可由逗號分隔的參數列表、->符號和語句塊組成,例如:

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );

Arrays.asList( "a", "b", "d" ).forEach( e -> {
    System.out.print( e );
    System.out.print( e );
} );


String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    ( String e ) -> System.out.print( e + separator ) );

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
等同於
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
  • 方法引用 − 方法引用提供了非常有用的語法,可以直接引用已有Java類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。

    Lambda表達式的一種特殊形式。當一個lambda表達式body中僅僅是調用某個方法,此時使用方法引用替代Lambda表達式。從形式上直接引用這個方法,比在lambda表達式body中引用在形式上更簡潔。

      方法引用通過方法的名字來指向一個方法。

      方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。

      方法引用使用一對冒號 :: 

Java 8使用兩個新概念擴展了接口的含義:默認方法和靜態方法。默認方法使得接口有點類似traits,不過要實現的目標不一樣。默認方法使得開發者可以在 不破壞二進制兼容性的前提下,往現存接口中添加新的方法,即不強制那些實現了該接口的類也同時實現這個新加的方法。

默認方法和抽象方法之間的區別在於抽象方法需要實現,而默認方法不需要。接口提供的默認方法會被接口的實現類繼承或者覆寫,例子代碼如下:

private interface Defaulable {
    default String notRequired() { 
        return "Default implementation"; 
    }        
}
 
private static class DefaultableImpl implements Defaulable {
}
 
private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
Defaulable接口使用關鍵字default定義了一個默認方法notRequired()。DefaultableImpl類實現了這個接口,同時默認繼承了這個接口中的默認方法;OverridableImpl類也實現了這個接口,但覆寫了該接口的默認方法,並提供了一個不同的實現。

Java 8帶來的另一個有趣的特性是在接口中可以定義靜態方法,例子代碼如下:

private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier< Defaulable > supplier ) {
        return supplier.get();
    }
}
下面的代碼片段整合了默認方法和靜態方法的使用場景:
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );
 
    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
輸出結果如下:
Default implementation
Overridden implementation

 

  • 默認方法 − 默認方法就是一個在接口里面有了一個實現的方法。

    主要目的是為了升級標准JDK接口,另外也是為了能在JDK8中順暢的使用Lamb的表達式。

private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier< Defaulable > supplier ) {
        return supplier.get();
    }
}
  • 新工具 − 新的編譯工具,如:Nashorn引擎 jjs、 類依賴分析器jdeps。

  • java批量數據操作bulk dataoperations

    目的是應用lambda函數來實現包含並行操作在內的多種數據處理功能,而支持並行數據操作是其關鍵內容。這個並行操作實在java7的java.util.concurrency的Fork/Join機制上實現的。

  • Stream API −新添加的Stream API(java.util.stream) 把真正的函數式編程風格引入到Java中。

  • Date Time API − 加強對日期與時間的處理。

  • Optional 類 − Optional 類已經成為 Java 8 類庫的一部分,用來解決空指針異常。

  • Nashorn, JavaScript 引擎 − Java 8提供了一個新的Nashorn javascript引擎,它允許我們在JVM上運行特定的javascript應用。

  • 重復注解:

    自從Java 5中引入注解以來,這個特性開始變得非常流行,並在各個框架和項目中被廣泛使用。不過,注解有一個很大的限制是:在同一個地方不能多次使用同一個注解。Java 8打破了這個限制,引入了重復注解的概念,允許在同一個地方多次使用同一個注解。

    在Java 8中使用@Repeatable注解定義重復注解,實際上,這並不是語言層面的改進,而是編譯器做的一個trick,底層的技術仍然相同。可以利用下面的代碼說明:

  • package com.javacodegeeks.java8.repeatable.annotations;
     
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
     
    public class RepeatingAnnotations {
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        public @interface Filters {
            Filter[] value();
        }
     
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        @Repeatable( Filters.class )
        public @interface Filter {
            String value();
        };
     
        @Filter( "filter1" )
        @Filter( "filter2" )
        public interface Filterable {        
        }
     
        public static void main(String[] args) {
            for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
                System.out.println( filter.value() );
            }
        }
    }

    正如我們所見,這里的Filter類使用@Repeatable(Filters.class)注解修飾,而Filters是存放Filter注解的容器,編譯器盡量對開發者屏蔽這些細節。這樣,Filterable接口可以用兩個Filter注解注釋(這里並沒有提到任何關於Filters的信息)。

    另外,反射API提供了一個新的方法:getAnnotationsByType(),可以返回某個類型的重復注解,例如Filterable.class.getAnnoation(Filters.class)將返回兩個Filter實例,輸出到控制台的內容如下所示: filter1 filter2

 

2、集合:

哈希沖突:

(見博客:https://www.cnblogs.com/wuchaodzxx/p/7396599.html)

如果兩個不同的元素,通過哈希函數得出的實際存儲地址相同怎么辦?也就是說,當我們對某個元素進行哈希運算,得到一個存儲地址,然后要進行插入的時候,發現已經被其他元素占用了,其實這就是所謂的哈希沖突,也叫哈希碰撞。

哈希函數的設計至關重要,好的哈希函數會盡可能地保證 計算簡單和散列地址分布均勻,但是,我們需要清楚的是,數組是一塊連續的固定長度的內存空間,再好的哈希函數也不能保證得到的存儲地址絕對不發生沖突。那么哈希沖突如何解決呢?哈希沖突的解決方案有多種:開放定址法(發生沖突,繼續尋找下一塊未被占用的存儲地址),再散列函數法,鏈地址法,而HashMap即是采用了鏈地址法,也就是數組+鏈表的方式。

(1)開放定址法

這種方法也稱再散列法,其基本思想是:當關鍵字key的哈希地址p=H(key)出現沖突時,以p為基礎,產生另一個哈希地址p1,如果p1仍然沖突,再以p為基礎,產生另一個哈希地址p2,…,直到找出一個不沖突的哈希地址pi ,將相應元素存入其中。這種方法有一個通用的再散列函數形式:

Hi=(H(key)+di% m   i=1,2,…,n

其中H(key)為哈希函數,m 為表長,di稱為增量序列。增量序列的取值方式不同,相應的再散列方式也不同。主要有以下三種:

線性探測再散列

dii=1,2,3,…,m-1

這種方法的特點是:沖突發生時,順序查看表中下一單元,直到找出一個空單元或查遍全表。

二次探測再散列

di=12-1222-22…,k2-k2    ( k<=m/2 )

這種方法的特點是:沖突發生時,在表的左右進行跳躍式探測,比較靈活。

偽隨機探測再散列

di=偽隨機數序列。

(2)再哈希法

這種方法是同時構造多個不同的哈希函數:

Hi=RH1key)  i=1,2,…,k

當哈希地址Hi=RH1key)發生沖突時,再計算Hi=RH2key)……,直到沖突不再產生。這種方法不易產生聚集,但增加了計算時間。

(3)鏈地址法

這種方法的基本思想是將所有哈希地址為i的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。

(4)建立公共溢出區

這種方法的基本思想是:將哈希表分為基本表和溢出表兩部分,凡是和基本表發生沖突的元素,一律填入溢出表。

 

HashMap的底層原理:

見博客:https://www.cnblogs.com/chengxiao/p/6059914.html

HashMap的主干是一個Entry數組。Entry是HashMap的基本組成單元,每一個Entry包含一個key-value鍵值對。

//HashMap的主干數組,可以看到就是一個Entry數組,初始值為空數組{},主干數組的長度一定是2的次冪,至於為什么這么做,后面會有詳細分析。
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

Entry是HashMap中的一個靜態內部類。代碼如下:

復制代碼
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;//存儲指向下一個Entry的引用,單鏈表結構
        int hash;//對key的hashcode值進行hash運算后得到的值,存儲在Entry,避免重復計算

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        } 
復制代碼

簡單來說,HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的,如果定位到的數組位置不含鏈表(當前entry的next指向null),那么對於查找,添加等操作很快,僅需一次尋址即可;如果定位到的數組包含鏈表,對於添加操作,其時間復雜度為O(n),首先遍歷鏈表,存在即覆蓋,否則新增;對於查找操作來講,仍需遍歷鏈表,然后通過key對象的equals方法逐一比對查找。所以,性能考慮,HashMap中的鏈表出現越少,性能才會越好。

 

自動排序的集合:

TreeSet(樹集)是一個有序集合,可以按照任何順序將元素插入該集合,當對該集合進行迭代時,各個值將自動以排序后的順序出現。TreeSet中的元素按照升序排列,缺省是按照自然順序進行排序,意味着TreeSet中的元素要實現Comparable接口,或者有一個自定義的比較器Comparator。

TreeSet底層使用的是TreeMap,TreeMap的底層實現是紅黑樹(詳細見http://www.cnblogs.com/xujian2014/p/4645943.html)。

 public TreeSet()
 {
        this(new TreeMap<E,Object>());
 }

使用示例:

復制代碼
public class Test
{
    public static void main(String[] args)
    {
        TreeSet<String> treeSet=new TreeSet<>();
        treeSet.add("Bili");
        treeSet.add("Amy");
        treeSet.add("cDy");
        for (String string : treeSet)
        {
            System.out.println(string);
        }
 }
復制代碼

  由於String類實現了Comparable接口,它的compareTo方法是按照字典順序來對字符串進行排序,所以結果如下:

注意:

  1、TreeSet的排列順序必須是全局順序,也就是說任何兩個元素都是必須可比的,同時只有當他們比較相同時才返回0。

  2、如果樹集包含了n個元素,那么平均需要進行log2n次比較,才能找到新元素的正確位置。

 

3、線程:

多線程安全運行:

在多個線程並發執行訪問同一個數據時,如果不采取相應的措施,將會是非常危險的。為避免這種情況發生,我們要將多個線程對同一數據的訪問同步,確保線程安全。

所謂同步(synchronization)就是指一個線程訪問數據時,其它線程不得對同一個數據進行訪問,即同一時刻只能有一個線程訪問該數據,當這一線程訪問結束時其它線程才能對這它進行訪問。同步最常見的方式就是使用鎖(Lock),也稱為線程鎖。鎖是一種非強制機制,每一個線程在訪問數據或資源之前,首先試圖獲取(Acquire)鎖,並在訪問結束之后釋放(Release)鎖。在鎖被占用時試圖獲取鎖,線程會進入等待狀態,直到鎖被釋放再次變為可用。

 

Java里面一般用以下幾種機制保證線程安全:

1.互斥同步鎖(悲觀鎖)

1)Synchorized

2)ReentrantLock

互斥同步鎖也叫做阻塞同步鎖,特征是會對沒有獲取鎖的線程進行阻塞。

要理解互斥同步鎖,首選要明白什么是互斥什么是同步。簡單的說互斥就是非你即我,同步就是順序訪問。互斥同步鎖就是以互斥的手段達到順序訪問的目的。操作系統提供了很多互斥機制比如信號量,互斥量,臨界區資源等來控制在某一個時刻只能有一個或者一組線程訪問同一個資源。

Java里面的互斥同步鎖就是Synchorized和ReentrantLock,前者是由語言級別實現的互斥同步鎖,理解和寫法簡單但是機制笨拙,在JDK6之后性能優化大幅提升,即使在競爭激烈的情況下也能保持一個和ReentrantLock相差不多的性能,所以JDK6之后的程序選擇不應該再因為性能問題而放棄synchorized。ReentrantLock是API層面的互斥同步鎖,需要程序自己打開並在finally中關閉鎖,和synchorized相比更加的靈活,體現在三個方面:等待可中斷,公平鎖以及綁定多個條件。但是如果程序猿對ReentrantLock理解不夠深刻,或者忘記釋放lock,那么不僅不會提升性能反而會帶來額外的問題。另外synchorized是JVM實現的,可以通過監控工具來監控鎖的狀態,遇到異常JVM會自動釋放掉鎖。而ReentrantLock必須由程序主動的釋放鎖。

互斥同步鎖都是可重入鎖,好處是可以保證不會死鎖。但是因為涉及到核心態和用戶態的切換,因此比較消耗性能。JVM開發團隊在JDK5-JDK6升級過程中采用了很多鎖優化機制來優化同步無競爭情況下鎖的性能。比如:自旋鎖和適應性自旋鎖,輕量級鎖,偏向鎖,鎖粗化和鎖消除。

2.非阻塞同步鎖

1) 原子類(CAS)

非阻塞同步鎖也叫樂觀鎖,相比悲觀鎖來說,它會先進行資源在工作內存中的更新,然后根據與主存中舊值的對比來確定在此期間是否有其他線程對共享資源進行了更新,如果舊值與期望值相同,就認為沒有更新,可以把新值寫回內存,否則就一直重試直到成功。它的實現方式依賴於處理器的機器指令:CAS(Compare And Swap)

JUC中提供了幾個Automic類以及每個類上的原子操作就是樂觀鎖機制。

不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常態。激烈的時候,Atomic的性能會優於ReentrantLock一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現一個Atomic的變量,多於一個同步無效。因為他不能在多個Atomic之間同步。 

非阻塞鎖是不可重入的,否則會造成死鎖。

3.無同步方案

1)可重入代碼

在執行的任何時刻都可以中斷-重入執行而不會產生沖突。特點就是不會依賴堆上的共享資源

2)ThreadLocal/Volaitile

線程本地的變量,每個線程獲取一份共享變量的拷貝,單獨進行處理。

3)  線程本地存儲

如果一個共享資源一定要被多線程共享,可以盡量讓一個線程完成所有的處理操作,比如生產者消費者模式中,一般會讓一個消費者完成對隊列上資源的消費。典型的應用是基於請求-應答模式的web服務器的設計

 

4、String和StringBuffer

(1)String:是對象不是原始類型.為不可變對象,一旦被創建,就不能修改它的值.對於已經存在的String對象的修改都是重新創建一個新的對象,然后把新的值保存進去.String 是final類,即不能被繼承

   String的值是不可變的,這就導致每次對String的操作都會生成新的String對象,不僅效率低下,而且大量浪費有限的內存空間。 
   String a = "a"; //假設a指向地址0x0001 
   a = "b";//重新賦值后a指向地址0x0002,但0x0001地址中保存的"a"依舊存在,但已經不再是a所指向的,a 已經指向了其它地址。 
   因此String的操作都是改變賦值地址而不是改變值操作。

(2)StringBuffer:是一個可變對象,當對他進行修改的時候不會像String那樣重新建立對象。它只能通過構造函數來建立對象被建立以后,在內存中就會分配內存空間,並初始保存一個null.向StringBuffer中賦值的時候可以通過它的append方法.

StringBuffer是可變類,和線程安全的字符串操作類,任何對它指向的字符串的操作都不會產生新的對象。 每個StringBuffer對象都有一定的緩沖區容量,當字符串大小沒有超過容量時,不會分配新的容量,當字符串大小超過容量時,會自動增加容量。 
   StringBuffer buf=new StringBuffer(); //分配長16字節的字符緩沖區 
   StringBuffer buf=new StringBuffer(512); //分配長512字節的字符緩沖區 
   StringBuffer buf=new StringBuffer("this is a test")//在緩沖區中存放了字符串,並在后面預留了16字節的空緩沖區。 

💗StringBuffer類中的方法要偏重於對字符串的變化例如追加、插入和刪除等,這個也是StringBuffer和String類的主要區別。
1、append方法
public StringBuffer append(boolean b)
該方法的作用是追加內容到當前StringBuffer對象的末尾,類似於字符串的連接。調用該方法以后,StringBuffer對象的內容也發生改變

2、deleteCharAt方法
public StringBuffer deleteCharAt(int index)
該方法的作用是刪除指定位置的字符,然后將剩余的內容形成新的字符串。例如:
StringBuffer sb = new StringBuffer(“Test”);
sb. deleteCharAt(1);
該代碼的作用刪除字符串對象sb中索引值為1的字符,也就是刪除第二個字符,剩余的內容組成一個新的字符串。所以對象sb的值變為”Tst”。
還存在一個功能類似的delete方法:
public StringBuffer delete(int start,int end)
該方法的作用是刪除指定區間以內的所有字符,包含start,不包含end索引值的區間。例如:
StringBuffer sb = new StringBuffer(“TestString”);
sb. delete (1,4);
該代碼的作用是刪除索引值1(包括)到索引值4(不包括)之間的所有字符,剩余的字符形成新的字符串。則對象sb的值是”TString”。

3、insert方法
public StringBuffer insert(int offset, String s)
該方法的作用是在StringBuffer對象中插入內容,然后形成新的字符串。例如:
StringBuffer sb = new StringBuffer(“TestString”);
sb.insert(4,“false”);
該示例代碼的作用是在對象sb的索引值4的位置插入字符串false,形成新的字符串,則執行以后對象sb的值是”TestfalseString”。
4、reverse方法
public StringBuffer reverse()
該方法的作用是將StringBuffer對象中的內容反轉,然后形成新的字符串。例如:
StringBuffer sb = new StringBuffer(“abc”);
sb.reverse();
經過反轉以后,對象sb中的內容將變為”cba”。

5、setCharAt方法
public void setCharAt(int index, char ch)
該方法的作用是修改對象中索引值為index位置的字符為新的字符ch。例如:
StringBuffer sb = new StringBuffer(“abc”);
sb.setCharAt(1,’D’);
則對象sb的值將變成”aDc”。

6、trimToSize方法
public void trimToSize()
該方法的作用是將StringBuffer對象的中存儲空間縮小到和字符串長度一樣的長度,減少空間的浪費。
7、構造方法:
StringBuffer s0=new StringBuffer();分配了長16字節的字符緩沖區
StringBuffer s1=new StringBuffer(512);分配了512字節的字符緩沖區
8、獲取字符串的長度: length()
StringBuffer s = new StringBuffer("www");
int i=s.length();
m.返回字符串的一部分值
substring(int start) //返回從start下標開始以后的字符串
substring(int start,int end) //返回從start到 end-1字符串
9.替換字符串
replace(int start,int end,String str)
s.replace(0,1,"qqq");
10.轉換為不變字符串:toString()。

StringBuffer和StringBuilder類功能基本相似,主要區別在於StringBuffer類的方法是多線程、安全的,而 StringBuilder不是線程安全的,相比而言,StringBuilder類會略微快一點。對於經常要改變值的字符串應該使用 StringBuffer和StringBuilder類。 
線程安全 
StringBuffer 線程安全 
StringBuilder 線程不安全 
速度 
一般情況下,速度從快到慢:StringBuilder>StringBuffer>String,這種比較是相對的,不是絕對的。 
總結 
(1)如果要操作少量的數據用 = String 
(2)單線程操作字符串緩沖區 下操作大量數據 = StringBuilder 
(3)多線程操作字符串緩沖區 下操作大量數據 = StringBuffer

 

JVM:

 1、運行流程

java程序經過一次編譯之后,將java代碼編譯為字節碼也就是class文件,然后在不同的操作系統上依靠不同的java虛擬機進行解釋,最后再轉換為不同平台的機器碼,最終得到執行。這樣我們是不是可以推演,如果要在mac系統上運行,是不是只需要安裝mac java虛擬機就行了。那么了解了這個基本原理后,我們嘗試去做更深的研究,一個普通的java程序它的執行流程到底是怎樣的呢?例如我們寫了一段這樣的代碼:

public class HelloWorld { public static void main(String[] args) { System.out.print("Hello world"); } }

這段程序從編譯到運行,最終打印出“Hello world”中間經過了哪些步驟呢?我們直接上圖:

2、內部結構

class文件被jvm裝載以后,經過jvm的內存空間調配,最終是由執行引擎完成class文件的執行。當然這個過程還有其他角色模塊的協助,這些模塊協同配合才能讓一個java程序成功的運行

 

3、JVM內存空間包含:方法區、java堆、java棧、本地方法棧。

方法區是各個線程共享的區域,存放類信息、常量、靜態變量。

java堆也是線程共享的區域,我們的類的實例就放在這個區域,可以想象你的一個系統會產生很多實例,因此java堆的空間也是最大的。如果java堆空間不足了,程序會拋出OutOfMemoryError異常。

java棧是每個線程私有的區域,它的生命周期與線程相同,一個線程對應一個java棧,每執行一個方法就會往棧中壓入一個元素,這個元素叫“棧幀”,而棧幀中包括了方法中的局部變量、用於存放中間狀態值的操作棧,這里面有很多細節,我們以后再講。如果java棧空間不足了,程序會拋出StackOverflowError異常,想一想什么情況下會容易產生這個錯誤,對,遞歸,遞歸如果深度很深,就會執行大量的方法,方法越多java棧的占用空間越大。

本地方法棧角色和java棧類似,只不過它是用來表示執行本地方法的,本地方法棧存放的方法調用本地方法接口,最終調用本地方法庫,實現與操作系統、硬件交互的目的。

PC寄存器,說到這里我們的類已經加載了,實例對象、方法、靜態變量都去了自己該去的地方,那么問題來了,程序該怎么執行,哪個方法先執行,哪個方法后執行,這些指令執行的順序就是PC寄存器在管,它的作用就是控制程序指令的執行順序。

執行引擎當然就是根據PC寄存器調配的指令順序,依次執行程序指令。

 

LINUX:

1、查找文件內容命令:

在使用linux時,經常需要進行文件查找。其中查找的命令主要有find和grep。兩個命令是有區的。

  區別:(1)find命令是根據文件的屬性進行查找,如文件名,文件大小,所有者,所屬組,是否為空,訪問時間,修改時間等。 

               (2)grep是根據文件的內容進行查找,會對文件的每一行按照給定的模式(patter)進行匹配查找。

一.find命令

    基本格式:find  path expression

    1.按照文件名查找

    (1)find / -name httpd.conf  #在根目錄下查找文件httpd.conf,表示在整個硬盤查找
    (2)find /etc -name httpd.conf  #在/etc目錄下文件httpd.conf
    (3)find /etc -name '*srm*'  #使用通配符*(0或者任意多個)。表示在/etc目錄下查找文件名中含有字符串‘srm’的文件
    (4)find . -name 'srm*'   #表示當前目錄下查找文件名開頭是字符串‘srm’的文件

    2.按照文件特征查找     

    (1)find / -amin -10   # 查找在系統中最后10分鍾訪問的文件(access time)
    (2)find / -atime -2   # 查找在系統中最后48小時訪問的文件
    (3)find / -empty   # 查找在系統中為空的文件或者文件夾
    (4)find / -group cat   # 查找在系統中屬於 group為cat的文件
    (5)find / -mmin -5   # 查找在系統中最后5分鍾里修改過的文件(modify time)
    (6)find / -mtime -1   #查找在系統中最后24小時里修改過的文件
    (7)find / -user fred   #查找在系統中屬於fred這個用戶的文件
    (8)find / -size +10000c  #查找出大於10000000字節的文件(c:字節,w:雙字,k:KB,M:MB,G:GB)
    (9)find / -size -1000k   #查找出小於1000KB的文件

    3.使用混合查找方式查找文件

    參數有: !,-and(-a),-or(-o)。

    (1)find /tmp -size +10000c -and -mtime +2   #在/tmp目錄下查找大於10000字節並在最后2分鍾內修改的文件
         (2)find / -user fred -or -user george   #在/目錄下查找用戶是fred或者george的文件文件
         (3)find /tmp ! -user panda  #在/tmp目錄中查找所有不屬於panda用戶的文件

二、grep命令

     基本格式:find  expression

命令:grep 
    格式:grep [option] pattern filenames 
    功能:逐行搜索所指定的文件或標准輸入,並顯示匹配模式的每一行。 
    選項:-i    匹配時忽略大小寫 
  -v 找出模式失配的行 
    例如:% grep -i 'java*' ./test/run.sh 

     1.主要參數

    [options]主要參數:
    -c:只輸出匹配行的計數。
    -i:不區分大小寫
    -h:查詢多文件時不顯示文件名。
    -l:查詢多文件時只輸出包含匹配字符的文件名。
    -n:顯示匹配行及行號。
    -s:不顯示不存在或無匹配文本的錯誤信息。
    -v:顯示不包含匹配文本的所有行。

    pattern正則表達式主要參數:
    \: 忽略正則表達式中特殊字符的原有含義。
    ^:匹配正則表達式的開始行。
    $: 匹配正則表達式的結束行。
    \<:從匹配正則表達 式的行開始。
    \>:到匹配正則表達式的行結束。
    [ ]:單個字符,如[A]即A符合要求 。
    [ - ]:范圍,如[A-Z],即A、B、C一直到Z都符合要求 。
    .:所有的單個字符。
    * :有字符,長度可以為0。

    2.實例  

  (1)grep 'test' d*  #顯示所有以d開頭的文件中包含 test的行
  (2)grep ‘test’ aa bb cc    #顯示在aa,bb,cc文件中包含test的行
  (3)grep ‘[a-z]\{5\}’ aa   #顯示所有包含每行字符串至少有5個連續小寫字符的字符串的行
  (4)grep magic /usr/src  #顯示/usr/src目錄下的文件(不含子目錄)包含magic的行
  (5)grep -r magic /usr/src  #顯示/usr/src目錄下的文件(包含子目錄)包含magic的行

  (6)grep -w pattern files :只匹配整個單詞,而不是字符串的一部分(如匹配’magic’,而不是’magical’),

 

2、GC狀態命令

通常運行命令如下:
jstat -gc 30996 3000
即:每3秒一次顯示進程號為30996的java進程的GC情況
或使用命令:jstat -gcutil 30996 3000
 
1.查進程
    ps命令查找與進程相關的PID號:
    ps a 顯示現行終端機下的所有程序,包括其他用戶的程序。
    ps -A 顯示所有程序。
 
2.殺進程
   使用kill命令結束進程:kill xxx
   常用:kill -9 324
 
 

大數據:

HDFS節點、角色

1.Namenode名稱節點

   目錄的管理者,每一個集群都有一個,記錄實時的數據變化,如果沒有namenode,HDFS就無法工作,系統中的文件將會全部丟失,就無法將位於不同datanode上的文件快(blocks)重建文件。因此它的容錯機制很有必要。

它主要負責:

  1. 接收用戶的請求;
  2. 維護文件系統的目錄結構;
  3. 管理文件與Block之間的練習;

2.Datanode數據節點

是文件系統的工作節點,他們根據客戶端或者是namenode的調度存儲和檢索,並且定期向namenode發送他們所存儲的塊(block)的列表。

集群中的每個服務器都運行一個DataNode后台程序,這個后台程序負責把HDFS數據塊讀寫到本地的文件系統。當需要通過客戶端讀/寫某個 數據時,先由NameNode告訴客戶端去哪個DataNode進行具體的讀/寫操作,然后,客戶端直接與這個DataNode服務器上的后台程序進行通 信,並且對相關的數據塊進行讀/寫操作。

它主要負責:

  1. 存放數據;
  2. 文件被分割以Block的形式被存儲在磁盤上;

3.Secondarynode

SecondaryNameNode是一個用來監控HDFS狀態的輔助后台程序。就想NameNode一樣,每個集群都有一個SecondaryNameNode,並且部署在一個單獨的服務器上。

SecondaryNameNode不同於NameNode,它不接受或者記錄任何實時的數據變化,但是,它會與NameNode進行通信,以便定期地保存HDFS元數據的快照。由於NameNode是單點的,通過SecondaryNameNode的快照功能,可以將NameNode的宕機時間和數據損失降低到最小。同時,如果NameNode發生問題,SecondaryNameNode可以及時地作為備用NameNode使用。

它主要將namenode image(fsimage)和Edit log合並的。

SecondaryNameNode的處理,是將fsimage和edites文件周期的合並,不會造成nameNode重啟時造成長時間不可訪問的情況。

4.Resourcemanager

(1)與客戶端進行交互,處理來自於客戶端的請求,如查詢應用的運行情況等。

(2)啟動和管理各個應用的ApplicationMaster,並且為ApplicationMaster申請第一個Container用於啟動和在它運行失敗時將它重新啟動。

(3)管理NodeManager,接收來自NodeManager的資源和節點健康情況匯報,並向NodeManager下達管理資源命令,例如kill掉某個container。

(4)資源管理和調度,接收來自ApplicationMaster的資源申請,並且為其進行分配。這個是它的最重要的職能。

5.Nodemanager

NM是ResourceManager在每台機器上的代理,負責容器管理,並監控它們的資源使用情況,以及向ResourceManager/Scheduler提供資源使用報告。

總結: 

 (1)NameNode與ResourceManager分開部署(都是老大)

(2)NodeManager 也就是Resoucemanager 的“小弟”,它來做這事情,讀取hdfs 上的數據,數據保存在datanode上,所以如果數據集群,datanode 與NodeManager ,一定要保存在同一個節點上

(3)Resoucemanager : 占用端口:8088   進行調度資源(老大),進行任務分配的,誰來做這個事情


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM