導航:首頁 > 方法技巧 > jvm知識導圖如何調用方法的

jvm知識導圖如何調用方法的

發布時間:2022-11-06 18:04:18

① Java中如何調用函數和自定義函數

調用函數

System.out.println("hello world");

println即為函數,out為是輸出對象,System為java系統類。

Java源程序(.java文件)——>java位元組碼文件(.class文件)——>由解釋執行器(java.exe)將位元組碼文件載入到java虛擬機(jvm)——>位元組碼文件(.class)就會在java虛擬機中執行。

Java的基本包
java.lang其中包含有:
介面:Comparable、Cloneable、Runable等
類:八個基本數據類型封裝類、Math、Runtime、Object、String、StringBuffer、Thread、Exception等

② Spark性能調優篇七之JVM相關參數調整

        由於Spark程序是運行在JVM基礎之上的,所以我們這一篇來討論一下關於JVM的一些優化操作。在開始JVM調優操作之前,我們先通過一張圖看一下JVM簡單的內存劃分情況。

        關於JVM內存的深入知識在這里不贅述,請大家自行對相關知識進行補充。好,說回Spark,運行Spark作業的時候,JVM對會對Spark作業產生什麼影響呢?答案很簡單,如果數據量過大,一定會導致JVM內存不足。在Spark作業運行時,會創建出來大量的對象,每一次將對象放入JVM時,首先將創建的對象都放入到eden區域和其中一個survivor區域中;當eden區域和一個survivor區域放滿了以後,這個時候會觸發minor gc,把不再使用的對象全部清除,而剩餘的對象放入另外一個servivor區域中。JVM中默認的eden,survivor1,survivor2的內存佔比為8:1:1。當存活的對象在一個servivor中放不下的時候,就會將這些對象移動到老年代。如果JVM的內存不夠大的話,就會頻繁的觸發minor gc,這樣會導致一些短生命周期的對象進入到老年代,老年代的對象不斷的囤積,最終觸發full gc。一次full gc會使得所有其他程序暫停很長時間。最終嚴重影響我們的Spark的性能和運行速度。

基於以上原因, 我們的第一個JVM優化點就是降低cache操作的內存佔比 ;

        spark中,堆內存又被劃分成了兩塊兒,一塊兒是專門用來給RDD的cache、persist操作進行RDD數據緩存用的;另外一塊兒,就是我們剛才所說的,用來給spark運算元函數的運行使用的,存放函數中自己創建的對象。默認情況下,給RDD cache操作的內存佔比是0.6,即60%的內存都給了cache操作了。但是問題是,如果某些情況下cache佔用的內存並不需要佔用那麼大,這個時候可以將其內存佔比適當降低。怎麼判斷在什麼時候調整RDD cache的內存佔用比呢?其實通過Spark監控平台就可以看到Spark作業的運行情況了,如果發現task頻繁的gc,就可以去調整cache的內存佔用比了。通過SparkConf.set("spark.storage.memoryFraction","0.6")來設定。

    我們第二個JVM優化點是 堆外內存和連接等待時長的調整 ;其實這兩個參數主要是為了解決一些Spark作業運行時候出現的一些錯誤信息而進行調整的。下面我們來分別介紹一下這兩個點。

1.堆外內存的調整

a) 問題提出

        有時候,如果你的spark作業處理的數據量特別特別大,幾億數據量;然後spark作業一運行就會出現類似shuffle file cannot find,executor、task lost,out of memory(內存溢出)等這樣的錯誤。這是因為可能是說executor的堆外內存不太夠用,導致executor在運行的過程中,可能會內存溢出;然後可能導致後續的stage的task在運行的時候,可能要從一些executor中去拉取shuffle map output文件,但是executor可能已經掛掉了,關聯的blockmanager也沒有了;所以可能會報shuffle  output file not found;resubmitting task;executor lost 這樣的錯誤;最終導致spark作業徹底崩潰。

        上述情況下,就可以去考慮調節一下executor的堆外內存。也許就可以避免報錯;此外,有時,堆外內存調節的比較大的時候,對於性能來說,也會帶來一定的提升。

b) 解決方案:

--conf  spark.yarn.executor.memoryOverhead=2048

        在spark-submit腳本裡面添加如上配置。默認情況下,這個堆外內存上限大概是300多M;我們通常項目中真正處理大數據的時候,這里都會出現問題導致spark作業反復崩潰無法運行;此時就會去調節這個參數,到至少1G或者更大的內存。通常這個參數調節上去以後,就會避免掉某些OOM的異常問題,同時呢,會讓整體spark作業的性能,得到較大的提升。

2.連接等待時長的調整

a) 問題提出:

        由於JVM內存過小,導致頻繁的Minor gc,有時候更會觸犯full gc,一旦出發full gc;此時所有程序暫停,導致無法建立網路連接;spark默認的網路連接的超時時長是60s;如果卡住60s都無法建立連接的話,那麼就宣告失敗了。碰到一種情況,有時候報錯信息會出現一串類似file id not found,file lost的錯誤。這種情況下,很有可能是task需要處理的那份數據的executor在正在進行gc。所以拉取數據的時候,建立不了連接。然後超過默認60s以後,直接宣告失敗。幾次都拉取不到數據的話,可能會導致spark作業的崩潰。也可能會導致DAGScheler,反復提交幾次stage。TaskScheler,反復提交幾次task。大大延長我們的spark作業的運行時間。

b) 解決方案:

--conf spark.core.connection.ack.wait.timeout=300

        在spark-submit腳本中添加如上參數,調節這個值比較大以後,通常來說,可以避免部分的偶爾出現的某某文件拉取失敗,某某文件lost掉的錯誤。

總結:本文關於JVM的相關調優暫時先到這里,關於Spark中的JVM相關知識後面會在trouble shutting的時候還會繼續介紹。後續還會不斷更新關於Spark作業優化的一些其他方式,歡迎關注。

z小趙    Spark性能調優之JVM相關參數調整

③ java中圖形界面ListModel的用法方法如何調用

AbstractListModel這個類的功能是抽象定義一個帶內容的list,你程序 MyListModel類中繼承並實現了這個抽象類,this.list1=new JList(new MyListModel());這幾句話中的new MyListMode()不就是實例化一個對象,這個對象就是一個帶內容的list,就是用這個對象的內容實例化一個JList。
希望我的回答對你有幫助,並及時採納

④ java思維導圖

Java虛擬機是Java語言的運行環境,它是Java別具吸引力的特性之一,屬於Java的中級內容。在學習過Java初級知識後,工程師就需要學習Java虛擬機。

周志明的《深入理解Java虛擬機》詳細的介紹了Java虛擬機,但是學習的過程中會發現書本很厚,知識點很多,我最開始是採用有道雲筆記去記筆記,但是發現知識點過於分散,朋友建議我繪制Java虛擬機的思維導圖,更有助於學習Java虛擬機。

⑤ 請描述一下JVM載入class文件的原理機制

原理:Java中的所有類,都需要由類載入器裝載到JVM中才能運行。類載入器本身也是一個類,而它的工作就是把class文件從硬碟讀取到內存中。

在寫程序的時候,我們幾乎不需要關心類的載入,因為這些都是隱式裝載的,除非我們有特殊的用法,像是反射,就需要顯式的載入所需要的類。

類裝載方式,有兩種 :

1、隱式裝載, 程序在運行過程中當碰到通過new 等方式生成對象時,隱式調用類裝載器載入對應的類到jvm中。

2、顯式裝載, 通過class.forname()等方法,顯式載入需要的類

Java類的載入是動態的,它並不會一次性將所有類全部載入後再運行,而是保證程序運行的基礎類(像是基類)完全載入到jvm中,至於其他類,則在需要的時候才載入。這當然就是為了節省內存開銷。

(5)jvm知識導圖如何調用方法的擴展閱讀:

Java的類載入器有三個,對應Java的三種類:

Bootstrap Loader :啟動類載入器,是虛擬機自身的一部分。負責將存放在lib目錄中的類庫載入到虛擬機中。其無法被Java程序直接引用。負責載入系統類 (指的是內置類,像是String,對應於C#中的System類和C/C++標准庫中的類)。

ExtClassLoader :負責載入擴展類(就是繼承類和實現類)。

AppClassLoader :負責載入用戶類路徑(ClassPath)上所指定的類庫(程序員自定義的類)。

JVM中類的載入是由類載入器(ClassLoader)和它的子類來實現的,Java中的類載入器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。

由於Java的跨平台性,經過編譯的Java源程序並不是一個可執行程序,而是一個或多個類文件。當Java程序需要使用某個類時,JVM會確保這個類已經被載入、連接(驗證、准備和解析)和初始化。

類的載入是指把類的.class文件中的數據讀入到內存中,通常是創建一個位元組數組讀入.class文件,然後產生與所載入類對應的Class對象。載入完成後,Class對象還不完整,所以此時的類還不可用。

當類被載入後就進入連接階段,這一階段包括

驗證:為了確保Class文件的位元組流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。

⑥ 理解JVM怎麼使用Windows和Linux下的本機內存

Java™ 堆耗盡並不是造成 java.lang.OutOfMemoryError 的惟一原因。如果本機內存 耗盡,則會發生普通調試技巧無法解決的OutOfMemoryError。本文將討論本機內存的概念,Java 運行時如何使用它,它被耗盡時會出現什麼情況,以及如何在 Windows® 和 Linux® 上調試本機 OutOfMemoryError。針對 AIX® 系統的相同主題將在 另一篇同類文章 中介紹。
Java 堆(每個 Java 對象在其中分配)是您在編寫 Java 應用程序時使用最頻繁的內存區域。JVM 設計用於將我們與主機的特性隔離,所以將內存當作堆來考慮再正常不過了。您一定遇到過 Java 堆 OutOfMemoryError ,它可能是由於對象泄漏造成的,也可能是因為堆的大小不足以存儲所有數據,您也可能了解這些場景的一些調試技巧。但是隨著您的 Java 應用程序處理越來越多的數據和越來越多的並發負載,您可能就會遇到無法使用常規技巧進行修復的 OutOfMemoryError。在一些場景中,即使 java 堆未滿,也會拋出錯誤。當這類場景發生時,您需要理解 Java 運行時環境(Java Runtime Environment,JRE)內部到底發生了什麼。
Java 應用程序在 Java 運行時的虛擬化環境中運行,但是運行時本身是使用 C 之類的語言編寫的本機程序,它也會耗用本機資源,包括本機內存。 本機內存是可用於運行時進程的內存,它與 Java 應用程序使用的 java 堆內存不同。每種虛擬化資源(包括 Java 堆和 Java 線程)都必須存儲在本機內存中,虛擬機在運行時使用的數據也是如此。這意味著主機的硬體和操作系統施加在本機內存上的限制會影響到 Java 應用程序的性能。
本系列文章共分兩篇,討論不同平台上的相應話題。本文是其中一篇。在這兩篇文章中,您將了解什麼是本機內存,Java 運行時如何使用它,本機內存耗盡之後會發生什麼情況,以及如何調試本機 OutOfMemoryError。本文介紹 Windows 和 Linux 平台上的這一主題,不會介紹任何特定的運行時實現。另一篇 類似的文章 介紹 AIX 上的這一主題,著重介紹 IBM® Developer Kit for Java。(另一篇文章中關於 IBM 實現的信息也適合於除 AIX 之外的平台,因此如果您在 Linux 上使用 IBM Developer Kit for Java,或使用 IBM 32-bit Runtime Environment for Windows,您會發現這篇文章也有用處)。
本機內存簡介
我將首先解釋一下操作系統和底層硬體給本機內存帶來的限制。如果您熟悉使用 C 等語言管理動態內存,那麼您可以直接跳到 下一節。
硬體限制
本機進程遇到的許多限制都是由硬體造成的,而與操作系統沒有關系。每台計算機都有一個處理器和一些隨機存取存儲器(RAM),後者也稱為物理內存。處理器將數據流解釋為要執行的指令,它擁有一個或多個處理單元,用於執行整數和浮點運算以及更高級的計算。處理器具有許多寄存器 —— 常快速的內存元素,用作被執行的計算的工作存儲,寄存器大小決定了一次計算可使用的最大數值。
處 理器通過內存匯流排連接到物理內存。物理地址(處理器用於索引物理 RAM 的地址)的大小限制了可以定址的內存。例如,一個 16 位物理地址可以定址 0x0000 到 0xFFFF 的內存地址,這個地址范圍包括 2^16 = 65536 個惟一的內存位置。如果每個地址引用一個存儲位元組,那麼一個 16 位物理地址將允許處理器定址 64KB 內存。
處理器被描述 為特定數量的數據位。這通常指的是寄存器大小,但是也存在例外,比如 32 位 390 指的是物理地址大小。對於桌面和伺服器平台,這個數字為 31、32 或 64;對於嵌入式設備和微處理器,這個數字可能小至 4。物理地址大小可以與寄存器帶寬一樣大,也可以比它大或小。如果在適當的操作系統上運行,大部分 64 位處理器可以運行 32 位程序。
表 1 列出了一些流行的 Linux 和 Windows 架構,以及它們的寄存器和物理地址大小:

表 1. 一些流行處理器架構的寄存器和物理地址大小

架構
寄存器帶寬(位)
物理地址大小(位)

(現代)Intel® x86
32 32
36,具有物理地址擴展(Pentium Pro 和更高型號)

x86 64
64 目前為 48 位(以後將會增大)

PPC64
64 在 POWER 5 上為 50 位

390 31 位
32 31

390 64 位
64 64

操作系統和虛擬內存
如果您編寫無需操作系統,直接在處理器上運行的應用程序,您可以使用處理器可以定址的所有內存(假設連接到了足夠的物理 RAM)。但是要使用多任務和硬體抽象等特性,幾乎所有人都會使用某種類型的操作系統來運行他們的程序。
在 Windows 和 Linux 等多任務操作系統中,有多個程序在使用系統資源。需要為每個程序分配物理內存區域來在其中運行。可以設計這樣一個操作系統:每個程序直接使用物理內存,並 且可以可靠地僅使用分配給它的內存。一些嵌入式操作系統以這種方式工作,但是這在包含多個未經過集中測試的應用程序的環境中是不切實際的,因為任何程序都 可能破壞其他程序或者操作系統本身的內存。
虛擬內存 允許多個進程共享物理內存,而且不會破壞彼此的數據。在具有虛擬內存的操作系統(比如 Windows、Linux 和許多其他操作系統)中,每個程序都擁有自己的虛擬地址空間 —— 一個邏輯地址區域,其大小由該系統上的地址大小規定(所以,桌面和伺服器平台的虛擬地址空間為 31、32 或 64 位)。進程的虛擬地址空間中的區域可被映射到物理內存、文件或任何其他可定址存儲。當數據未使用時,操作系統可以在物理內存與一個交換區域 (Windows 上的頁面文件 或者 Linux 上的交換分區)之間移動它,以實現對物理內存的最佳利用率。當一個程序 嘗試使用虛擬地址訪問內存時,操作系統連同片上硬體會將該虛擬地址映射到物理位置,這個位置可以是物理 RAM、一個文件或頁面文件/交換分區。如果一個內存區域被移動到交換空間,那麼它將在被使用之前載入回物理內存中。圖 1 展示了虛擬內存如何將進程地址空間區域映射到共享資源:

程序的每個實例以進程 的形式運行。在 Linux 和 Windows 上,進程是一個由受操作系統控制的資源(比如文件和套接字信息)、一個典型的虛擬地址空間(在某些架構上不止一個)和至少一個執行線程構成的集合。
虛 擬地址空間大小可能比處理器的物理地址大小更小。32 位 Intel x86 最初擁有的 32 位物理地址僅允許處理器定址 4GB 存儲空間。後來,添加了一種稱為物理地址擴展(Physical Address Extension,PAE)的特性,將物理地址大小擴大到了 36 位,允許安裝或定址至多 64GB RAM。PAE 允許操作系統將 32 位的 4GB 虛擬地址空間映射到一個較大的物理地址范圍,但是它不允許每個進程擁有 64GB 虛擬地址空間。這意味著如果您將大於 4GB 的內存放入 32 位 Intel 伺服器中,您將無法將所有內存直接映射到一個單一進程中。
地址窗口擴展(Address Windowing Extension)特性允許 Windows 進程將其 32 位地址空間的一部分作為滑動窗口映射到較大的內存區域中。Linux 使用類似的技術將內存區域映射到虛擬地址空間中。這意味著盡管您無法直接引用大於 4GB 的內存,但您仍然可以使用較大的內存區域。
內核空間和用戶空間
盡管每個進程都有其自己的地址空間,但程序通常無法使用所有這些空間。地址空間被劃分為用戶空間 和內核空間。內核是主要的操作系統程序,包含用於連接計算機硬體、調度程序以及提供聯網和虛擬內存等服務的邏輯。
作為計算機啟動序列的一部分,操作系統內核運行並初始化硬體。一旦內核配置了硬體及其自己的內部狀態,第一個用戶空間進程就會啟動。如果用戶程序需要來自操作系統的服務,它可以執行一種稱為系統調用 的操作與內核程序交互,內核程序然後執行該請求。系統調用通常是讀取和寫入文件、聯網和啟動新進程等操作所必需的。
當 執行系統調用時,內核需要訪問其自己的內存和調用進程的內存。因為正在執行當前線程的處理器被配置為使用地址空間映射來為當前進程映射虛擬地址,所以大部 分操作系統將每個進程地址空間的一部分映射到一個通用的內核內存區域。被映射來供內核使用的地址空間部分稱為內核空間,其餘部分稱為用戶空間,可供用戶應 用程序使用。
內核空間和用戶空間之間的平衡關系因操作系統的不同而不同,甚至在運行於不同硬體架構之上的同一操作系統的各個實例 間也有所不同。這種平衡通常是可配置的,可進行調整來為用戶應用程序或內核提供更多空間。縮減內核區域可能導致一些問題,比如能夠同時登錄的用戶數量限制 或能夠運行的進程數量限制。更小的用戶空間意味著應用程序編程人員只能使用更少的內存空間。
默認情況下,32 位 Windows 擁有 2GB 用戶空間和 2GB 內核空間。在一些 Windows 版本上,通過向啟動配置添加 /3GB 開關並使用/LARGEADDRESSAWARE 開關重新鏈接應用程序,可以將這種平衡調整為 3GB 用戶空間和 1GB 內核空間。在 32 位 Linux 上,默認設置為 3GB 用戶空間和 1GB 內核空間。一些 Linux 分發版提供了一個 hugemem 內核,支持 4GB 用戶空間。為了實現這種配置,將進行系統調用時使用的地址空間分配給內核。通過這種方式增加用戶空間會減慢系統調用,因為每次進行系統調用時,操作系統必 須在地址空間之間復制數據並重置進程地址-空間映射。圖 2 展示了 32 位 Windows 的地址-空間布局:

31 位 Linux 390 上還使用了一個獨立的內核地址空間,其中較小的 2GB 地址空間使對單個地址空間進行劃分不太合理,但是,390 架構可以同時使用多個地址空間,而且不會降低性能。
進 程空間必須包含程序需要的所有內容,包括程序本身和它使用的共享庫(在 Windows 上為 DDL,在 Linux 上為 .so 文件)。共享庫不僅會占據空間,使程序無法在其中存儲數據,它們還會使地址空間碎片化,減少可作為連續內存塊分配的內存。這對於在擁有 3GB 用戶空間的 Windows x86 上運行的程序尤為明顯。DLL 在構建時設置了首選的載入地址:當載入 DLL 時,它被映射到處於特定位置的地址空間,除非該位置已經被佔用,在這種情況下,它會載入到別處。Windows NT 最初設計時設置了 2GB 可用用戶空間,這對於要構建來載入接近 2GB 區域的系統庫很有用 —— 使大部分用戶區域都可供應用程序自由使用。當用戶區域擴展到 3GB 時,系統共享庫仍然載入接近 2GB 數據(約為用戶空間的一半)。盡管總體用戶空間為 3GB,但是不可能分配 3GB 大的內存塊,因為共享庫無法載入這么大的內存。
在 Windows 中使用 /3GB 開關,可以將內核空間減少一半,也就是最初設計的大小。在一些情形下,可能耗盡 1GB 內核空間,使 I/O 變得緩慢,且無法正常創建新的用戶會話。盡管 /3GB 開關可能對一些應用程序非常有用,但任何使用它的環境在部署之前都應該進行徹底的負載測試。參見 參考資料,獲取關於 /3GB 開關及其優缺點的更多信息的鏈接。
本 機內存泄漏或過度使用本機內存將導致不同的問題,具體取決於您是耗盡了地址空間還是用完了物理內存。耗盡地址空間通常只會發生在 32 位進程上,因為最大 4GB 的內存很容易分配完。64 位進程具有數百或數千 GB 的用戶空間,即使您特意消耗空間也很難耗盡這么大的空間。如果您確實耗盡了 Java 進程的地址空間,那麼 Java 運行時可能會出現一些陌生現象,本文稍後將詳細討論。當在進程地址空間比物理內存大的系統上運行時,內存泄漏或過度使用本機內存會迫使操作系統交換後備存 儲器來用作本機進程的虛擬地址空間。訪問經過交換的內存地址比讀取駐留(在物理內存中)的地址慢得多,因為操作系統必須從硬碟驅動器拉取數據。可能會分配 大量內存來用完所有物理內存和所有交換內存(頁面空間),在 Linux 上,這將觸發內核內存不足(OOM)結束程序,強制結束最消耗內存的進程。在 Windows 上,與地址空間被占滿時一樣,內存分配將會失敗。
同時,如果嘗試使用比物理內存大的虛擬內存,顯然在進程由於消 耗內存太大而被結束之前就會遇到問題。系統將變得異常緩慢,因為它會將大部分時間用於在內存與交換空間之間來回復制數據。當發生這種情況時,計算機和獨立 應用程序的性能將變得非常糟糕,從而使用戶意識到出現了問題。當 JVM 的 Java 堆被交換出來時,垃圾收集器的性能會變得非常差,應用程序可能被掛起。如果一台機器上同時使用了多個 Java 運行時,那麼物理內存必須足夠分配給所有 Java 堆。

Java 運行時如何使用本機內存
Java 運行時是一個操作系統進程,它會受到我在上一節中列出的硬體和操作系統局限性的限制。運行時環境提供的功能受一些未知的用戶代碼驅動,這使得無法預測在每 種情形中運行時環境將需要何種資源。Java 應用程序在託管 Java 環境中執行的每個操作都會潛在地影響提供該環境的運行時的需求。本節描述 Java 應用程序為什麼和如何使用本機內存。
Java 堆和垃圾收集
Java 堆是分配了對象的內存區域。大多數 Java SE 實現都擁有一個邏輯堆,但是一些專家級 Java 運行時擁有多個堆,比如實現 Java 實時規范(Real Time Specification for Java,RTSJ)的運行時。一個物理堆可被劃分為多個邏輯扇區,具體取決於用於管理堆內存的垃圾收集(GC)演算法。這些扇區通常實現為連續的本機內存 塊,這些內存塊受 Java 內存管理器(包含垃圾收集器)控制。
堆的大小可以在 Java 命令行使用 -Xmx 和 -Xms 選項來控制(mx 表示堆的最大大小,ms 表示初始大小)。盡管邏輯堆(經常被使用的內存區域)可以根據堆上的對象數量和在 GC 上花費的時間而增大和縮小,但使用的本機內存大小保持不變,而且由 -Xmx 值(最大堆大小)指定。大部分 GC 演算法依賴於被分配為連續的內存塊的堆,因此不能在堆需要擴大時分配更多本機內存。所有堆內存必須預先保留。
保留本機內存與分配本機內存不同。當本機內存被保留時,無法使用物理內存或其他存儲器作為備用內存。盡管保留地址空間塊不會耗盡物理資源,但會阻止內存被用於其他用途。由保留從未使用的內存導致的泄漏與泄漏分配的內存一樣嚴重。
當使用的堆區域縮小時,一些垃圾收集器會回收堆的一部分(釋放堆的後備存儲空間),從而減少使用的物理內存。
對於維護 Java 堆的內存管理系統,需要更多本機內存來維護它的狀態。當進行垃圾收集時,必須分配數據結構來跟蹤空閑存儲空間和記錄進度。這些數據結構的確切大小和性質因實現的不同而不同,但許多數據結構都與堆大小成正比。
即時 (JIT) 編譯器
JIT 編譯器在運行時編譯 Java 位元組碼來優化本機可執行代碼。這極大地提高了 Java 運行時的速度,並且支持 Java 應用程序以與本機代碼相當的速度運行。
位元組碼編譯使用本機內存(使用方式與 gcc 等靜態編譯器使用內存來運行一樣),但 JIT 編譯器的輸入(位元組碼)和輸出(可執行代碼)必須也存儲在本機內存中。包含多個經過 JIT 編譯的方法的 Java 應用程序會使用比小型應用程序更多的本機內存。
類和類載入器
Java 應用程序由一些類組成,這些類定義對象結構和方法邏輯。Java 應用程序也使用 Java 運行時類庫(比如 java.lang.String)中的類,也可以使用第三方庫。這些類需要存儲在內存中以備使用。
存 儲類的方式取決於具體實現。Sun JDK 使用永久生成(permanent generation,PermGen)堆區域。Java 5 的 IBM 實現會為每個類載入器分配本機內存塊,並將類數據存儲在其中。現代 Java 運行時擁有類共享等技術,這些技術可能需要將共享內存區域映射到地址空間。要理解這些分配機制如何影響您 Java 運行時的本機內存佔用,您需要查閱該實現的技術文檔。然而,一些普遍的事實會影響所有實現。
從最基本的層面來看,使用更多的類將 需要使用更多內存。(這可能意味著您的本機內存使用量會增加,或者您必須明確地重新設置 PermGen 或共享類緩存等區域的大小,以裝入所有類)。記住,不僅您的應用程序需要載入到內存中,框架、應用伺服器、第三方庫以及包含類的 Java 運行時也會按需載入並佔用空間。
Java 運行時可以卸載類來回收空間,但是只有在非常嚴酷的條件下才會這樣做。不能卸載單個類,而是卸載類載入器,隨其載入的所有類都會被卸載。只有在以下情況下才能卸載類載入器:
Java 堆不包含對表示該類載入器的 java.lang.ClassLoader 對象的引用。
Java 堆不包含對表示類載入器載入的類的任何 java.lang.Class 對象的引用。
在 Java 堆上,該類載入器載入的任何類的所有對象都不再存活(被引用)。
需要注意的是,Java 運行時為所有 Java 應用程序創建的 3 個默認類載入器( bootstrap、extension 和 application )都不可能滿足這些條件,因此,任何系統類(比如 java.lang.String)或通過應用程序類載入器載入的任何應用程序類都不能在運行時釋放。
即使類載入器適合進行收集,運行時也只會將收集類載入器作為 GC 周期的一部分。一些實現只會在某些 GC 周期中卸載類載入器。
也 可能在運行時生成類,而不用釋放它。許多 JEE 應用程序使用 JavaServer Pages (JSP) 技術來生成 Web 頁面。使用 JSP 會為執行的每個 .jsp 頁面生成一個類,並且這些類會在載入它們的類載入器的整個生存期中一直存在 —— 這個生存期通常是 Web 應用程序的生存期。
另一種生成類的常見方法是使用 Java 反射。反射的工作方式因 Java 實現的不同而不同,但 Sun 和 IBM 實現都使用了這種方法,我馬上就會講到。
當使用 java.lang.reflect API 時,Java 運行時必須將一個反射對象(比如 java.lang.reflect.Field) 的方法連接到被反射到的對象或類。這可以通過使用 Java 本機介面(Java Native Interface,JNI)訪問器來完成,這種方法需要的設置很少,但是速度緩慢。也可以在運行時為您想要反射到的每種對象類型動態構建一個類。後一種 方法在設置上更慢,但運行速度更快,非常適合於經常反射到一個特定類的應用程序。
Java 運行時在最初幾次反射到一個類時使用 JNI 方法,但當使用了若干次 JNI 方法之後,訪問器會膨脹為位元組碼訪問器,這涉及到構建類並通過新的類載入器進行載入。執行多次反射可能導致創建了許多訪問器類和類載入器。保持對反射對象 的引用會導致這些類一直存活,並繼續佔用空間。因為創建位元組碼訪問器非常緩慢,所以 Java 運行時可以緩存這些訪問器以備以後使用。一些應用程序和框架還會緩存反射對象,這進一步增加了它們的本機內存佔用。
JNI
JNI 支持本機代碼(使用 C 和 C++ 等本機編譯語言編寫的應用程序)調用 Java 方法,反之亦然。Java 運行時本身極大地依賴於 JNI 代碼來實現類庫功能,比如文件和網路 I/O。JNI 應用程序可能通過 3 種方式增加 Java 運行時的本機內存佔用:
JNI 應用程序的本機代碼被編譯到共享庫中,或編譯為載入到進程地址空間中的可執行文件。大型本機應用程序可能僅僅載入就會佔用大量進程地址空間。

本機代碼必須與 Java 運行時共享地址空間。任何本機代碼分配或本機代碼執行的內存映射都會耗用 Java 運行時的內存。

某些 JNI 函數可能在它們的常規操作中使用本機內存。GetTypeArrayElements 和 GetTypeArrayRegion 函數可以將 Java 堆數據復制到本機內存緩沖區中,以供本機代碼使用。是否復制數據依賴於運行時實現。(IBM Developer Kit for Java 5.0 和更高版本會進行本機復制)。通過這種方式訪問大量 Java 堆數據可能會使用大量本機堆。

⑦ Java中如何在無參構造方法中調用有參構造方法,希望最好有個例子,謝謝

一般正常的都是參數多的調用參數少的。有參數的調用無參數的居多。

當然你要無參調用的參的也可以。

你用無參,調用有參的。那你有參的參數你需要怎麼傳呢。當然如果換一個角度也可以。

我手寫下。沒經過IDE的。提供下思路。我有兩種思路,寫同一個功能。

public class Person{

private String name;

private int age;

//無參

public Person(){

this.name="張三";

this.age=20;

}

//有參

public Person(String name){

this.name=name;

}

//多參

public Person(String name,int age){

this(name);//調用一個參數的構造方法

this.age=age;

}

}

這是一種寫法比較常用的方法。寫第一種。用上面的例子直接寫

public Person(){

this("張三",20);//調用有參構造方法。設置默認值。和第一種方法功能一樣

}

public Person(String name,int age){

this.name=name;

this.age=age;

}

為了程序易讀性。一般。參數的擴展和構造方法的調用都是有一定的規律的。

有參調無參,多參調少參。。擴展參數的時候,最好保持原來的順序。

⑧ JVM原理是什麼

首先這里澄清兩個概念:JVM實例和JVM執行引擎實例,JVM實例對應了一個獨立運行的Java程序,而JVM執行引擎實例則對應了屬於用戶運行程序的線程;也就是JVM實例是進程級別,而執行引擎是線程級別的。JVM是什麼?—JVM的生命周期JVM實例的誕生:當啟動一個Java程序時,一個JVM實例就產生了,任何一個擁有publicstaticvoidmain(String[]args)函數的class都可以作為JVM實例運行的起點,既然如此,那麼JVM如何知道是運行classA的main而不是運行classB的main呢?這就需要顯式的告訴JVM類名,也就是我們平時運行Java程序命令的由來,如JavaclassAhelloworld,這里Java是告訴os運行SunJava2SDK的Java虛擬機,而classA則指出了運行JVM所需要的類名。JVM實例的運行:main()作為該程序初始線程的起點,任何其他線程均由該線程啟動。JVM內部有兩種線程:守護線程和非守護線程,main()屬於非守護線程,守護線程通常由JVM自己使用,Java程序也可以標明自己創建的線程是守護線程。JVM實例的消亡:當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出。JVM是什麼?—JVM的體系結構粗略分來,JVM的內部體系結構分為三部分,分別是:類裝載器(ClassLoader)子系統,運行時數據區,和執行引擎。下面將先介紹類裝載器,然後是執行引擎,最後是運行時數據區1、類裝載器,顧名思義,就是用來裝載.class文件的。JVM的兩種類裝載器包括:啟動類裝載器和用戶自定義類裝載器,啟動類裝載器是JVM實現的一部分,用戶自定義類裝載器則是Java程序的一部分,必須是ClassLoader類的子類。(下面所述情況是針對SunJDK1.2)動類裝載器:只在系統類(JavaAPI的類文件)的安裝路徑查找要裝入的類用戶自定義類裝載器:系統類裝載器:在JVM啟動時創建,用來在CLASSPATH目錄下查找要裝入的類其他用戶自定義類裝載器:這里有必要先說一下ClassLoader類的幾個方法,了解它們對於了解自定義類裝載器如何裝載.class文件至關重要。(Stringname,bytedata[],intoffset,intlength) (Stringname,bytedata[],intoffset,intlength,);(Stringname) (Classc) defineClass用來將二進制class文件(新類型)導入到方法區,也就是這里指的類是用戶自定義的類(也就是負責裝載類)findSystemClass通過類型的全限定名,先通過系統類裝載器或者啟動類裝載器來裝載,並返回Class對象。ResolveClass:讓類裝載器進行連接動作(包括驗證,分配內存初始化,將類型中的符號引用解析為直接引用),這里涉及到Java命名空間的問題,JVM保證被一個類裝載器裝載的類所引用的所有類都被這個類裝載器裝載,同一個類裝載器裝載的類之間可以相互訪問,但是不同類裝載器裝載的類看不見對方,從而實現了有效的屏蔽。2、執行引擎:它或者在執行位元組碼,或者執行本地方法要說執行引擎,就不得不的指令集,每一條指令包含一個單位元組的操作碼,後面跟0個或者多個操作數。(一)指令集以棧為設計中心,而非以寄存器為中心這種指令集設計如何滿足Java體系的要求:平台無關性:以棧為中心使得在只有很少register的機器上實現Java更便利compiler一般採用stack向連接優化器傳遞編譯的中間結果,若指令集以stack為基礎,則有利於運行時進行的優化工作與執行即時編譯或者自適應優化的執行引擎結合,通俗的說就是使編譯和運行用的數據結構統一,更有利於優化的開展。網路移動性:class文件的緊湊性。安全性:指令集中絕大部分操作碼都指明了操作的類型。(在裝載的時候使用數據流分析期進行一次性驗證,而非在執行每條指令的時候進行驗證,有利於提高執行速度)。(二)執行技術主要的執行技術有:解釋,即時編譯,自適應優化、晶元級直接執行其中解釋屬於第一代JVM,即時編譯JIT屬於第二代JVM,自適應優化(目前Sun的HotspotJVM採用這種技術)則吸取第一代JVM和第二代JVM的經驗,採用兩者結合的方式自適應優化:開始對所有的代碼都採取解釋執行的方式,並監視代碼執行情況,然後對那些經常調用的方法啟動一個後台線程,將其編譯為本地代碼,並進行仔細優化。若方法不再頻繁使用,則取消編譯過的代碼,仍對其進行解釋執行。3、運行時數據區:主要包括:方法區,堆,Java棧,PC寄存器,本地方法棧(1)方法區和堆由所有線程共享堆:存放所有程序在運行時創建的對象方法區:當JVM的類裝載器載入.class文件,並進行解析,把解析的類型信息放入方法區。(2)Java棧和PC寄存器由線程獨享,在新線程創建時間里(3)本地方法棧:存儲本地方法調用的狀態上邊總體介紹了運行時數據區的主要內容,下邊進行詳細介紹,要介紹數據區,就不得不說明JVM中的數據類型。JVM中的數據類型:JVM中基本的數據單元是word,而word的長度由JVM具體的實現者來決定數據類型包括基本類型和引用類型,(1)基本類型包括:數值類型(包括除boolean外的所有的Java基本數據類型),boolean(在JVM中使用int來表示,0表示false,其他int值均表示true)和returnAddress(JVM的內部類型,用來實現finally子句)。(2)引用類型包括:數組類型,類類型,介面類型前邊講述了JVM中數據的表示,下面讓我們輸入到JVM的數據區首先來看方法區:上邊已經提到,方法區主要用來存儲JVM從class文件中提取的類型信息,那麼類型信息是如何存儲的呢?眾所周知,Java使用的是大端序(big?endian:即低位元組的數據存儲在高位內存上,如對於1234,12是高位數據,34為低位數據,則Java中的存儲格式應該為12存在內存的低地址,34存在內存的高地址,x86中的存儲格式與之相反)來存儲數據,這實際上是在class文件中數據的存儲格式,但是當數據倒入到方法區中時,JVM可以以任何方式來存儲它。類型信息:包括class的全限定名,class的直接父類,類類型還是介面類型,類的修飾符(public,等),所有直接父介面的列表,Class對象提供了訪問這些信息的窗口(可通過Class.forName(「」)或instance.getClass()獲得),下面是Class的方法,相信大家看了會恍然大悟,(原來如此J)getName(),getSuperClass(),isInterface(),getInterfaces(),getClassLoader();static變數作為類型信息的一部分保存指向ClassLoader類的引用:在動態連接時裝載該類中引用的其他類指向Class類的引用:必然的,上邊已述該類型的常量池:包括直接常量(String,integer和floatpoint常量)以及對其他類型、欄位和方法的符號引用(注意:這里的常量池並不是普通意義上的存儲常量的地方,這些符號引用可能是我們在編程中所接觸到的變數),由於這些符號引用,使得常量池成為Java程序動態連接中至關重要的部分欄位信息:普通意義上的類型中聲明的欄位方法信息:類型中各個方法的信息編譯期常量:指用final聲明或者用編譯時已知的值初始化的類變數class將所有的常量復制至其常量池或者其位元組碼流中。方法表:一個數組,包括所有它的實例可能調用的實例方法的直接引用(包括從父類中繼承來的)除此之外,若某個類不是抽象和本地的,還要保存方法的位元組碼,操作數棧和該方法的棧幀,異常表。舉例:classLava{ privateintspeed=5; voidflow(){} classVolcano{ publicstaticvoidmain(String[]args){ Lavalava=newLava(); lava.flow(); } } 運行命令JavaVolcano;(1)JVM找到Volcano.class倒入,並提取相應的類型信息到方法區。通過執行方法區中的位元組碼,JVM執行main()方法,(執行時會一直保存指向Vocano類的常量池的指針)(2)Main()中第一條指令告訴JVM需為列在常量池第一項的類分配內存(此處再次說明了常量池並非只存儲常量信息),然後JVM找到常量池的第一項,發現是對Lava類的符號引用,則檢查方法區,看Lava類是否裝載,結果是還未裝載,則查找「Lava.class」,將類型信息寫入方法區,並將方法區Lava類信息的指針來替換Volcano原常量池中的符號引用,即用直接引用來替換符號引用。(3)JVM看到new關鍵字,准備為Lava分配內存,根據Volcano的常量池的第一項找到Lava在方法區的位置,並分析需要多少對空間,確定後,在堆上分配空間,並將speed變數初始為0,並將lava對象的引用壓到棧中(4)調用lava的flow()方法好了,大致了解了方法區的內容後,讓我們來看看堆Java對象的堆實現:Java對象主要由實例變數(包括自己所屬的類和其父類聲明的)以及指向方法區中類數據的指針,指向方法表的指針,對象鎖(非必需),等待集合(非必需),GC相關的數據(非必需)(主要視GC演算法而定,如對於標記並清除演算法,需要標記對象是否被引用,以及是否已調用finalize()方法)。那麼為什麼Java對象中要有指向類數據的指針呢?我們從幾個方面來考慮首先:當程序中將一個對象引用轉為另一個類型時,如何檢查轉換是否允許?需用到類數據其次:動態綁定時,並不是需要引用類型,而是需要運行時類型,這里的迷惑是:為什麼類數據中保存的是實際類型,而非引用類型?這個問題先留下來,我想在後續的讀書筆記中應該能明白指向方法表的指針:這里和C++的VTBL是類似的,有利於提高方法調用的效率對象鎖:用來實現多個線程對共享數據的互斥訪問等待集合:用來讓多個線程為完成共同目標而協調功過。(注意Object類中的wait(),notify(),notifyAll()方法)。Java數組的堆實現:數組也擁有一個和他們的類相關聯的Class實例,具有相同dimension和type的數組是同一個類的實例。數組類名的表示:如[[LJava/lang/Object表示Object[][],[I表示int[],[[[B表示byte[][][]至此,堆已大致介紹完畢,下面來介紹程序計數器和Java棧程序計數器:為每個線程獨有,在線程啟動時創建,若thread執行Java方法,則PC保存下一條執行指令的地址。若thread執行native方法,則Pc的值為undefinedJava棧:Java棧以幀為單位保存線程的運行狀態,Java棧只有兩種操作,幀的壓棧和出棧。每個幀代表一個方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會導致該方法對應的幀出棧和釋放內存。幀的組成:局部變數區(包括方法參數和局部變數,對於instance方法,還要首先保存this類型,其中方法參數按照聲明順序嚴格放置,局部變數可以任意放置),操作數棧,幀數據區(用來幫助支持常量池的解析,正常方法返回和異常處理)。本地方法棧:依賴於本地方法的實現,如某個JVM實現的本地方法借口使用C連接模型,則本地方法棧就是C棧,可以說某線程在調用本地方法時,就進入了一個不受JVM限制的領域,也就是JVM可以利用本地方法來動態擴展本身。相信大家都明白JVM是什麼了吧。原文鏈接: http://www.cnblogs.com/chenzhao/archive/2011/08/14/2137713.html

⑨ jvm的理解

1
JVM內存區域

我們在編寫程序時,經常會遇到OOM(out of Memory)以及內存泄漏等問題。為了避免出現這些問題,我們首先必須對JVM的內存劃分有個具體的認識。JVM將內存主要劃分為:方法區、虛擬機棧、本地方法棧、堆、程序計數器。JVM運行時數據區如下:

1.1
程序計數器
程序計數器是線程私有的區域,很好理解嘛~,每個線程當然得有個計數器記錄當前執行到那個指令。佔用的內存空間小,可以把它看成是當前線程所執行的位元組碼的行號指示器。如果線程在執行Java方法,這個計數器記錄的是正在執行的虛擬機位元組碼指令地址;如果執行的是Native方法,這個計數器的值為空(Undefined)。

此內存區域是唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。

1.2
Java虛擬機棧
與程序計數器一樣,Java虛擬機棧也是線程私有的,其生命周期與線程相同。

如何理解虛擬機棧呢?

本質上來講,就是個棧。裡面存放的元素叫棧幀,棧幀好像很復雜的樣子,其實它很簡單!它裡面存放的是一個函數的上下文,具體存放的是執行的函數的一些數據。執行的函數需要的數據無非就是局部變數表(保存函數內部的變數)、操作數棧(執行引擎計算時需要),方法出口等等。

執行引擎每調用一個函數時,就為這個函數創建一個棧幀,並加入虛擬機棧。換個角度理解,每個函數從調用到執行結束,其實是對應一個棧幀的入棧和出棧。

注意這個區域可能出現的兩種異常:

一種是StackOverflowError,當前線程請求的棧深度大於虛擬機所允許的深度時,會拋出這個異常。製造這種異常很簡單:將一個函數反復遞歸自己,最終會出現棧溢出錯誤(StackOverflowError)。

另一種異常是OutOfMemoryError異常,當虛擬機棧可以動態擴展時(當前大部分虛擬機都可以),如果無法申請足夠多的內存就會拋出OutOfMemoryError,如何製作虛擬機棧OOM呢,參考一下代碼:

這段代碼有風險,可能會導致操作系統假死,請謹慎使用~~~

1.3
本地方法棧
本地方法棧與虛擬機所發揮的作用很相似,他們的區別在於虛擬機棧為執行Java代碼方法服務,而本地方法棧是為Native方法服務。與虛擬機棧一樣,本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。

1.4
Java堆
Java堆可以說是虛擬機中最大一塊內存了。它是所有線程所共享的內存區域,幾乎所有的實例對象都是在這塊區域中存放。當然,隨著JIT編譯器的發展,所有對象在堆上分配漸漸變得不那麼「絕對」了。

Java堆是垃圾收集器管理的主要區域。由於現在的收集器基本上採用的都是分代收集演算法,所有Java堆可以細分為:新生代和老年代。在細致分就是把新生代分為:Eden空間、From Survivor空間、To Survivor空間。當堆無法再擴展時,會拋出OutOfMemoryError異常。

1.5
方法區
方法區存放的是類信息、常量、靜態變數等。方法區是各個線程共享區域,很容易理解,我們在寫Java代碼時,每個線程度可以訪問同一個類的靜態變數對象。由於使用反射機制的原因,虛擬機很難推測那個類信息不再使用,因此這塊區域的回收很難。另外,對這塊區域主要是針對常量池回收,值得注意的是JDK1.7已經把常量池轉移到堆裡面了。同樣,當方法區無法滿足內存分配需求時,會拋出OutOfMemoryError。

製造方法區內存溢出,注意,必須在JDK1.6及之前版本才會導致方法區溢出,原因後面解釋,執行之前,可以把虛擬機的參數-XXpermSize和-XX:MaxPermSize限制方法區大小。

運行後會拋出java.lang.OutOfMemoryError:PermGen space異常。

解釋一下,String的intern()函數作用是如果當前的字元串在常量池中不存在,則放入到常量池中。上面的代碼不斷將字元串添加到常量池,最終肯定會導致內存不足,拋出方法區的OOM。

下面解釋一下,為什麼必須將上面的代碼在JDK1.6之前運行。我們前面提到,JDK1.7後,把常量池放入到堆空間中,這導致intern()函數的功能不同,具體怎麼個不同法,且看看下面代碼:

這段代碼在JDK1.6和JDK1.7運行的結果不同。

JDK1.6結果是:false,false ,JDK1.7結果是true, false。

原因是:JDK1.6中,intern()方法會吧首次遇到的字元串實例復制到常量池中,返回的也是常量池中的字元串的引用,而StringBuilder創建的字元串實例是在堆上面,所以必然不是同一個引用,返回false。

在JDK1.7中,intern不再復制實例,常量池中只保存首次出現的實例的引用,因此intern()返回的引用和由StringBuilder創建的字元串實例是同一個。為什麼對str2比較返回的是false呢?這是因為,JVM中內部在載入類的時候,就已經有"java"這個字元串,不符合「首次出現」的原則,因此返回false。

⑩ 怎樣在ide中進行jvm源碼的調試

開發調試環境: Ubuntu 14.10,openJdk 1.7,Android Studio 1.0.2,android-5.0.1_r1源碼
由於AS是基於IntelliJ IDEA開發的,所以本文也適用於IntelliJ IDEA
一、修改Android Studio(以下簡稱AS)的內存配置

因為在導入源碼時需要消耗大量內存,所以先修改IDEA_HOME/bin/studio64.vmoptions(x86的機器修改studio.vmoptions)中-Xms和-Xmx的值。文檔中使用的是748m, 可自行修改。
二、配置AS的JDK、SDK
在IDE中添加一個沒有classpath的JDK, 這樣可以確保使用源碼里的庫文件

並將其作為要使用的SDK的Java SDK。如下圖

三、生成導入AS所需配置文件(*.ipr)
①編譯源碼(為了確保生成了.java文件,如R.java;如果編譯過,則無需再次編譯)
②檢查out/host/linux-x86/framework/目錄下是否有idegen.jar
如果idegen.jar不存在,執行:
mmm development/tools/idegen/

在5.0.1的源碼中會生成res.java的文件夾,導致idegen.jar運行時拋FileNotFoundException,這是idegen的代碼不夠嚴謹造成的。
我的分享里有修改這個bug的patch,或者直接使用我分享的idegen.jar。
③執行
development/tools/idegen/idegen.sh

等待出現類似下面的結果:
Read excludes: 5ms
Traversed tree: 44078ms

這時會在源碼的根目錄下生成android.ipr和android.iml兩個IntelliJ IDEA(AS是基於IntelliJ IDEA社區版開發的)的配置文件
Tips:
AS在導入代碼時比較慢,建議先修改android.iml,將自己用不到的代碼exclude出去.可以仿照過濾.repo文件夾的語法,如:
<excludeFolder url="file://$MODULE_DIR$/.repo" />
<excludeFolder url="file://$MODULE_DIR$/abi" />
<excludeFolder url="file://$MODULE_DIR$/art" />
這樣在導入時就會跳過abi和art文件夾.過濾的越多,AS的處理速度就會越快.
④在AS中打開源碼根目錄下新生成的android.ipr

閱讀全文

與jvm知識導圖如何調用方法的相關的資料

熱點內容
數字電路通常用哪三種方法分析 瀏覽:9
實訓課程的教學方法是什麼 瀏覽:519
苯甲醇乙醚鑒別方法 瀏覽:76
蘋果手機微信視頻聲音小解決方法 瀏覽:694
控制箱的連接方法 瀏覽:69
用什麼簡單的方法可以去痘 瀏覽:783
快速去除甲醛的小方法你知道幾個 瀏覽:798
自行車架尺寸測量方法 瀏覽:118
石磨子的製作方法視頻 瀏覽:146
行善修心的正確方法 瀏覽:400
土豆燉雞湯的正確方法和步驟 瀏覽:272
北京電流檢測方法 瀏覽:481
手機u盤保護方法 瀏覽:114
數字搭配有哪些方法 瀏覽:666
約一場球的正確方法 瀏覽:188
在家中洗衣服的方法如何 瀏覽:293
28天鍛煉腹肌最快的方法 瀏覽:203
簡單練翹臀方法視頻 瀏覽:760
心理診斷評估常用的方法有哪些 瀏覽:845
什麼方法能讓手機不黑屏 瀏覽:723