×

手寫JAVA虛擬機(二)——實現(xiàn)java命令行

  • 作者:新網(wǎng)
  • 來源:新網(wǎng)
  • 瀏覽:100
  • 2018-04-26 15:48:45

咱們都知道,咱們編譯.java并運轉(zhuǎn).class文件時,需求一些java指令,如最簡略的helloworld程序。java初學者可以看一下下面的教程。

   t0168e141ebeb2a01ea.jpg

<div>  咱們都知道,咱們編譯.java并運轉(zhuǎn).class文件時,需求一些java指令,如最簡略的helloworld程序。java初學者可以看一下下面的教程。
  這兒的程序最好不要加包名,因為加了包名的話編譯和運轉(zhuǎn)需求有所改動。
  看這兒的指令。javac為編譯指令,咱們知道java的特點是一次編譯,處處運轉(zhuǎn)。這兒的編譯指的就是javac,關于java程序即.java文件,先要用javac編譯成字節(jié)碼。然后將字節(jié)碼(.class文件)放到java虛擬機中運轉(zhuǎn),即上圖中的java HelloWorld,java虛擬機把字節(jié)碼翻譯成對應機器上的機器指令,再由機器來履行詳細的機器指令。也就是說java程序員是直接與java虛擬機交互,簡介與機器交互。所以虛擬機完結(jié)的是java指令,也就是咱們要完結(jié)的是java這個指令的功用。
  那么咱們把榜首個方針定為,完結(jié)簡略的指令行。即咱們經(jīng)過指令行能夠輸入一些內(nèi)容,虛擬機讀取之后能夠給必定的反應。
  GO言語中有兩個和指令行相關的包,分別是os和flag(java中以類庫即jar文件導入,go中直接以包的辦法導入)。
  首先在GOPATH目錄下的src里邊新建一個jvmgo文件夾作為咱們的作業(yè)空間目錄,jvmgo里邊再新建一個ch01為咱們的榜首個方針源碼文件夾,增加cmd.go文件。
  在cmd.go里邊輸入如下代碼(因為博客園的增加代碼辦法不支持go言語上色,所以選用C言語上色,高亮可能不太正確)
  package main import "flag" import "fmt" import "os" //界說Cmd結(jié)構體 type Cmd struct{ helpFlag bool versionFlag bool cpOption string class string args []string } //解析指令行參數(shù) func parseCmd() *Cmd { cmd:=&Cmd{} //將printUsage函數(shù)傳給flag.Usage flag.Usage=printUsage //設置各種解析的選項 flag.BoolVar(&cmd.helpFlag, "help", false, "print help message") flag.BoolVar(&cmd.helpFlag, "?", false, "print help message") flag.BoolVar(&cmd.versionFlag, "version", false, "print version and exit") flag.StringVar(&cmd.cpOption, "classpath", "", "classpath") flag.StringVar(&cmd.cpOption, "cp", "", "classpath") //一切選項設置完結(jié)后調(diào)用flag.Parse解析一切選項,假如Parse失利,則調(diào)用flag.Usage打印協(xié)助信息 flag.Parse() //調(diào)用flag.Args函數(shù)捕獲未被解析的參數(shù),榜首個參數(shù)為主類名,后邊的為傳遞給主類的參數(shù) args:=flag.Args() if len(args)>0{ cmd.class=args[0] cmd.args=args[1:] } return cmd } func printUsage() { fmt.Printf("Usage:%s[-options] class [args...] ",os.Args[0]) }
  榜首行為包名,main包,接著引入了三個包os,flag,fmt。os和flag都是處理指令行所需的包,fmt類似于C言語的printf和scanf等格式化IO。再往下界說了一個結(jié)構體Cmd,用來這個數(shù)據(jù)結(jié)構來格式化存儲輸入的指令行信息。helpFlag參數(shù)為指令行是否懇求help,versionFlag參數(shù)為指令行是否懇求version,cpOption為指令行傳入的classpath即方針.class文件地點文件夾,class為指令行傳入的.class文件名(不包括.class),args為指令行傳入的其他參數(shù)。
  緊接著是一個parseCmd函數(shù)(go言語有函數(shù)和辦法之分,辦法調(diào)用需求receiver,函數(shù)調(diào)用則不需求 ),回來值為*Cmd,用來解析cmd傳過來的參數(shù)。該函數(shù)里邊先聲明一個cmd并給這個cmd賦值一個新建的Cmd對象。go言語中的“:=”為聲明并賦值,而"="為賦值。先把printUsage的函數(shù)賦值給flag.Usage,然后調(diào)用flag設置需求解析的選項,悉數(shù)解析結(jié)束,調(diào)用Parse函數(shù)解析一切選項。解析成功則結(jié)束,解析失利則調(diào)用printUsage打印到控制臺。
  flag.Args能夠捕獲其他沒有被解析的參數(shù)。上面解析成功之后,榜首個參數(shù)就是主類名,剩余的就是傳給主類的參數(shù)。
  東西類編寫完結(jié),下一個是
  主函數(shù)。先上主函數(shù)代碼:
  package main import "fmt" func main() { //調(diào)用parseCmd解析指令行參數(shù) cmd:=parseCmd() if cmd.versionFlag{ //輸入了-version選項 fmt.Println("version 0.0.1") }else if cmd.helpFlag||cmd.class==""{ //輸入了-help選項 printUsage() }else{ //發(fā)動jvm stratJVM(cmd) } } func stratJVM(cmd *Cmd){ fmt.Printf("classpath:%s class:%s args:%v ", cmd.cpOption,cmd.class,cmd.args) }
  跟java類似,在go里邊main是一個特殊的包,go程序的入口就是main函數(shù),可是不接受任何參數(shù),也不能有回來值。main函數(shù)先調(diào)用parseCmd解析指令行參數(shù),假如是-version則回來版本號,假如是-help則回來協(xié)助信息,假如是其他則發(fā)動jvm,這兒用一些輸出信息“偽裝”發(fā)動了jvm,真正的jvm代碼后邊會加上。
  至此,對指令行的解析作業(yè)悉數(shù)完結(jié)。先展現(xiàn)一下整個作業(yè)目錄的結(jié)構,不然后邊編譯運轉(zhuǎn)的時分會犯錯。
  咱們的作業(yè)目錄是D盤下的JVM里的goWorkSpace,再下面src,jvmgo,ch01,ch01里邊包括的是咱們的go文件。
  來測驗一下,翻開一指令行,輸入go install jvmgoch01。這個指令是運用go.exe來install文件,這個文件存在于GOPATH下面的文件夾(jvmgoch01中),結(jié)果如圖:
  然后在作業(yè)空間(GOPATH)的bin文件夾中就多出了一個ch01.exe。
  在此處翻開指令行。能夠進行一些操作:
  到這兒,咱們的指令行東西就完結(jié)了,盡管還沒有觸及真正的虛擬機規(guī)劃,但這也是虛擬機運轉(zhuǎn)的重要一步,后邊會逐漸介紹虛擬機的規(guī)劃。
  同理,如果在unlock操作中,就是釋放了鎖,然后unpark,這兒就不詳細講了。
  咱們知道HashMap不是一個線程安全的容器,最簡略的辦法使HashMap變成線程安全就是運用Collections.synchronizedMap,它是對HashMap的一個包裝
  public static Map m=Collections.synchronizedMap(new HashMap());
  同理關于List,Set也供給了類似辦法。
  可是這種辦法只適合于并發(fā)量比較小的狀況。
  咱們來看下synchronizedMap的完成
  它會將HashMap包裝在里面,然后將HashMap的每個操作都加上synchronized。
  由于每個辦法都是獲取同一把鎖(mutex),這就意味著,put和remove等操作是互斥的,大大減少了并發(fā)量。
  下面來看下ConcurrentHashMap是怎么完成的
  在 ConcurrentHashMap內(nèi)部有一個Segment段,它將大的HashMap切分成若干個段(小的HashMap),然后讓數(shù)據(jù)在每一段上Hash,這樣多個線程在不同段上的Hash操作一定是線程安全的,所以只需要同步同一個段上的線程就可以了,這樣完成了鎖的別離,大大增加了并發(fā)量。
  在運用ConcurrentHashMap.size時會比較費事,由于它要計算每個段的數(shù)據(jù)和,在這個時分,要把每一個段都加上鎖,然后再做數(shù)據(jù)計算。這個就是把鎖別離后的小小壞處,可是size辦法應該是不會被高頻率調(diào)用的辦法。
  在完成上,不運用synchronized和lock.lock而是盡量運用trylock,一起在HashMap的完成上,也做了一點優(yōu)化。這兒就不提了。
 

免責聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻自行上傳,本網(wǎng)站不擁有所有權,也不承認相關法律責任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請發(fā)送郵件至:operations@xinnet.com進行舉報,并提供相關證據(jù),一經(jīng)查實,本站將立刻刪除涉嫌侵權內(nèi)容。

免費咨詢獲取折扣

Loading