一個普通的
操作系統,即類似于一個軟件中間件,是用戶程序和硬件之間的“中介”
因此,一個達到要求的操作系統,便能夠運行許多用戶級別的應用程序(第三方程序)。
同時,這些應用程序可以:
- 和用戶交互
- 和文件系統交互
對于應用程序來說,操作系統就像是計算機本身,這臺“計算機”提供了:
(1)進程(應用程序的可執行單元);
(2)內存;
(3)文件系統(位于磁盤中);
(4)互聯網交互;
(5)用戶交互設備(即輸入輸出設備,常見的諸如鍵盤,鼠標,顯示器等,專用的設備諸如空間球,游戲手柄等);
(6)對于多用戶操作系統,還需要給每個用戶分配權限,管理他們可以操作等文件等;
(7)計算機安全,比如防止黑客入侵某個部分,或者防止第三方應用程序的肆意破壞= =;
(8)其它。(Whatever needed)
(日后補充操作系統的架構概念圖)
因此,當我們學習操作系統時,可以從一下幾個方面入手: (1)內存管理與CPU:
首當其沖,這是操作系統,也就是應用程序所謂的“計算機”的核心。
(2)進程與線程:
這是最重要的部分,事實上和內存管理、CPU唇齒相連。沒有了進程和線程,操作系統也就失去了意義。
(3)文件系統:
對于一個操作系統,管理磁盤也相當重要;進程相關的內容都存儲在內存中,可是進程需要讀寫,需要長期存儲一些東西,這些都是文件系統都功勞。
(4)硬件設備接口:
硬件設備,包括磁盤,鼠標,鍵盤等,是操作系統的另一個重要任務;對于磁盤,關系到文件系統的讀取;而對于其它設備,關系到操作系統能否良好地提供一個交互式應用程序環境。
(5)互聯網接口:
這關系到操作系統能否提供一個良好的網絡應用程序環境。面對互聯網發達的今天,沒有一個操作系統可以不提供這樣的接口。
(注:Chromium OS,Firefox OS,幾乎完全依賴與網絡。Web OS,我認為是以后操作系統發展的方向)
在有了以上幾個部分以后,其實操作系統已經可以運行。但是為了更好的服務與應用程序,操作系統需要這些:
*虛擬內存:可以在磁盤中擴張內存。當應用程序需要的內存大于物理內存時,操作系統可以不受束縛的提供更大的內存。
*計算機安全:為了保護操作系統、硬盤、用戶資料或者別的用戶進程,操作系統應該需要提供安全保護。
堆內存設置
原理
JVM堆內存分為2塊:Permanent Space 和 Heap Space。
Permanent 即 持久代(Permanent Generation),主要存放的是
Java類定義信息,與垃圾收集器要收集的Java對象關系不大。
Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年輕代(Young Generation)。年老代和年輕代的劃分對垃圾收集影響比較大。
年輕代
所有新生成的對象首先都是放在年輕代。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。年輕代一般分3個區,1個Eden區,2個Survivor區(from 和 to)。
大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當一個Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當另一個Survivor區也滿了的時候,從前一個Survivor區復制過來的并且此時還存活的對象,將可能被復制到年老代。
2個Survivor區是對稱的,沒有先后關系,所以同一個Survivor區中可能同時存在從Eden區復制過來對象,和從另一個Survivor區復制過來的對象;而復制到年老區的只有從另一個Survivor區過來的對象。而且,因為需要交換的原因,Survivor區至少有一個是空的。特殊的情況下,根據程序需要,Survivor區是可以配置為多個的(多于2個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。
針對年輕代的垃圾回收即 Young GC。
年老代
在年輕代中經歷了N次(可配置)垃圾回收后仍然存活的對象,就會被復制到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。
針對年老代的垃圾回收即 Full GC。
持久代
用于存放靜態類型數據,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響。但是有些應用可能動態生成或調用一些Class,例如 Hibernate CGLib 等,在這種時候往往需要設置一個比較大的持久代空間來存放這些運行過程中動態增加的類型。
所以,當一組對象生成時,內存申請過程如下:
JVM會試圖為相關Java對象在年輕代的Eden區中初始化一塊內存區域。
當Eden區空間足夠時,內存申請結束。否則執行下一步。
JVM試圖釋放在Eden區中所有不活躍的對象(Young GC)。釋放后若Eden空間仍然不足以放入新對象,JVM則試圖將部分Eden區中活躍對象放入Survivor區。
Survivor區被用來作為Eden區及年老代的中間交換區域。當年老代空間足夠時,Survivor區中存活了一定次數的對象會被移到年老代。
當年老代空間不夠時,JVM會在年老代進行完全的垃圾回收(Full GC)。
Full GC后,若Survivor區及年老代仍然無法存放從Eden區復制過來的對象,則會導致JVM無法在Eden區為新生成的對象申請內存,即出現“Out of Memory”。
OOM(“Out of Memory”)異常一般主要有如下2種原因:
1. 年老代溢出,表現為:java.lang.OutOfMemoryError:Javaheapspace
這是最常見的情況,產生的原因可能是:設置的內存參數Xmx過小或程序的內存泄露及使用不當問題。
例如循環上萬次的字符串處理、創建上千萬個對象、在一段代碼內申請上百M甚至上G的內存。還有的時候雖然不會報內存溢出,卻會使系統不間斷的垃圾回收,也無法處理其它請求。這種情況下除了檢查程序、打印堆內存等方法排查,還可以借助一些內存分析工具,比如MAT就很不錯。
2. 持久代溢出,表現為:java.lang.OutOfMemoryError:PermGenspace
通常由于持久代設置過小,動態加載了大量Java類而導致溢出,解決辦法唯有將參數 -XX:MaxPermSize 調大(一般256m能滿足絕大多數應用程序需求)。將部分Java類放到容器共享區(例如Tomcat share lib)去加載的辦法也是一個思路,但前提是容器里部署了多個應用,且這些應用有大量的共享類庫。 參數說明
-Xmx3550m:設置JVM最大堆內存為3550M。
-Xms3550m:設置JVM初始堆內存為3550M。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。
-Xss128k:設置每個線程的棧大小。JDK5.0以后每個線程棧大小為1M,之前每個線程棧大小為256K。應當根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。需要注意的是:當這個值被設置的較大(例如>2MB)時將會在很大程度上降低系統的性能。
-Xmn2g:設置年輕代大小為2G。在整個堆內存大小確定的情況下,增大年輕代將會減小年老代,反之亦然。此值關系到JVM垃圾回收,對系統性能影響較大,官方推薦配置為整個堆大小的3/8。
-XX:NewSize=1024m:設置年輕代初始值為1024M。
-XX:MaxNewSize=1024m:設置年輕代最大值為1024M。
-XX:PermSize=256m:設置持久代初始值為256M。
-XX:MaxPermSize=256m:設置持久代最大值為256M。
-XX:NewRatio=4:設置年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示年輕代比年老代為1:4。
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的比值。表示2個Survivor區(JVM堆內存年輕代中默認有2個大小相等的Survivor區)與1個Eden區的比值為2:4,即1個Survivor區占整個年輕代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一個對象如果在Survivor區(救助空間)移動了7次還沒有被垃圾回收就進入年老代。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代,對于需要大量常駐內存的應用,這樣做可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象在年輕代存活時間,增加對象在年輕代被垃圾回收的概率,減少Full GC的頻率,這樣做可以在某種程度上提高服務穩定性。
疑問解答
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3組參數都可以影響年輕代的大小,混合使用的情況下,優先級是什么?
如下:
高優先級:-XX:NewSize/-XX:MaxNewSize
中優先級:-Xmn(默認等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低優先級:-XX:NewRatio
推薦使用-Xmn參數,原因是這個參數簡潔,相當于一次設定 NewSize/MaxNewSIze,而且兩者相等,適用于生產環境。-Xmn 配合 -Xms/-Xmx,即可將堆內存布局完成。
-Xmn參數是在JDK 1.4 開始支持。
垃圾回收器選擇
JVM給出了3種選擇:串行收集器、并行收集器、并發收集器。串行收集器只適用于小數據量的情況,所以生產環境的選擇主要是并行收集器和并發收集器。
默認情況下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數。JDK5.0以后,JVM會根據當前系統配置進行智能判斷。
串行收集器
-XX:+UseSerialGC:設置串行收集器。
并行收集器(吞吐量優先)
-XX:+UseParallelGC:設置為并行收集器。此配置僅對年輕代有效。即年輕代使用并行收集,而年老代仍使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的線程數,即:同時有多少個線程一起進行垃圾回收。此值建議配置與CPU數目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0開始支持對年老代并行收集。
-XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間(單位毫秒)。如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此時間。
-XX:+UseAdaptiveSizePolicy:設置此選項后,并行收集器會自動調整年輕代Eden區大小和Survivor區大小的比例,以達成目標系統規定的最低響應時間或者收集頻率等指標。此參數建議在使用并行收集器時,一直打開。
并發收集器(響應時間優先)
-XX:+UseConcMarkSweepGC:即CMS收集,設置年老代為并發收集。CMS收集是JDK1.4后期版本開始引入的新GC算法。它的主要適合場景是對響應時間的重要性需求大于對吞吐量的需求,能夠承受垃圾回收線程和應用線程共享CPU資源,并且應用中存在比較多的長生命周期對象。CMS收集的目標是盡量減少應用的暫停時間,減少Full GC發生的幾率,利用和應用程序線程并發的垃圾回收線程來標記清除年老代內存。
-XX:+UseParNewGC:設置年輕代為并發收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此參數。
-XX:CMSFullGCsBeforeCompaction=0:由于并發收集器不對內存空間進行壓縮和整理,所以運行一段時間并行收集以后會產生內存碎片,內存使用效率降低。此參數設置運行0次Full GC后對內存空間進行壓縮和整理,即每次Full GC后立刻開始壓縮和整理內存。
-XX:+UseCMSCompactAtFullCollection:打開內存空間的壓縮和整理,在Full GC后執行。可能會影響性能,但可以消除內存碎片。
-XX:+CMSIncrementalMode:設置為增量收集模式。一般適用于單CPU情況。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代內存空間使用到70%時就開始執行CMS收集,以確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發生。
其它垃圾回收參數
-XX:+ScavengeBeforeFullGC:年輕代GC優于Full GC執行。
-XX:-DisableExplicitGC:不響應 System.gc() 代碼。
-XX:+UseThreadPriorities:啟用本地線程優先級API。即使 java.lang.Thread.setPriority() 生效,不啟用則無效。
-XX:SoftRefLRUPolicyMSPerMB=0:軟引用對象在最后一次被訪問后能存活0毫秒(JVM默認為1000毫秒)。
-XX:TargetSurvivorRatio=90:允許90%的Survivor區被占用(JVM默認為50%)。提高對于Survivor區的使用率。
輔助信息參數設置
-XX:-CITime:打印消耗在JIT編譯的時間。
-XX:ErrorFile=./hs_err_pid.log:保存錯誤日志或數據到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆內存時的路徑。
-XX:-HeapDumpOnOutOfMemoryError:當首次遭遇內存溢出時Dump出此時的堆內存。
-XX:OnError=";":出現致命ERROR后運行自定義命令。
-XX:OnOutOfMemoryError=";":當首次遭遇內存溢出時執行自定義命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆內存中類實例的柱狀信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印線程棧中并發鎖的相關信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:當一個方法被編譯時打印相關信息。
-XX:-PrintGC:每次GC時打印相關信息。
-XX:-PrintGCDetails:每次GC時打印詳細信息。
-XX:-PrintGCTimeStamps:打印每次GC的時間戳。
-XX:-TraceClassLoading:跟蹤類的加載信息。
-XX:-TraceClassLoadingPreorder:跟蹤被引用到的所有類的加載信息。
-XX:-TraceClassResolution:跟蹤常量池。
-XX:-TraceClassUnloading:跟蹤類的卸載信息。
關于參數名稱等
標準參數(-),所有JVM都必須支持這些參數的功能,而且向后兼容;例如:
-client——設置JVM使用Client模式,特點是啟動速度比較快,但運行時性能和內存管理效率不高,通常用于客戶端應用程序或開發調試;在32位環境下直接運行Java程序默認啟用該模式。
-server——設置JVM使Server模式,特點是啟動速度比較慢,但運行時性能和內存管理效率很高,適用于生產環境。在具有64位能力的JDK環境下默認啟用該模式。
非標準參數(-X),默認JVM實現這些參數的功能,但是并不保證所有JVM實現都滿足,且不保證向后兼容;
非穩定參數(-XX),此類參數各個JVM實現會有所不同,將來可能會不被支持,需要慎重使用;
參數說明 -Xmx3550m:設置JVM最大堆內存為3550M。
-Xms3550m:設置JVM初始堆內存為3550M。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。
-Xss128k:設置每個線程的棧大小。JDK5.0以后每個線程棧大小為1M,之前每個線程棧大小為256K。應當根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。需要注意的是:當這個值被設置的較大(例如>2MB)時將會在很大程度上降低系統的性能。
-Xmn2g:設置年輕代大小為2G。在整個堆內存大小確定的情況下,增大年輕代將會減小年老代,反之亦然。此值關系到JVM垃圾回收,對系統性能影響較大,官方推薦配置為整個堆大小的3/8。
-XX:NewSize=1024m:設置年輕代初始值為1024M。
-XX:MaxNewSize=1024m:設置年輕代最大值為1024M。
-XX:PermSize=256m:設置持久代初始值為256M。
-XX:MaxPermSize=256m:設置持久代最大值為256M。
-XX:NewRatio=4:設置年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示年輕代比年老代為1:4。
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的比值。表示2個Survivor區(JVM堆內存年輕代中默認有2個大小相等的Survivor區)與1個Eden區的比值為2:4,即1個Survivor區占整個年輕代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一個對象如果在Survivor區(救助空間)移動了7次還沒有被垃圾回收就進入年老代。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代,對于需要大量常駐內存的應用,這樣做可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象在年輕代存活時間,增加對象在年輕代被垃圾回收的概率,減少Full GC的頻率,這樣做可以在某種程度上提高服務穩定性。
疑問解答
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3組參數都可以影響年輕代的大小,混合使用的情況下,優先級是什么?
如下:
高優先級:-XX:NewSize/-XX:MaxNewSize
中優先級:-Xmn(默認等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低優先級:-XX:NewRatio
推薦使用-Xmn參數,原因是這個參數簡潔,相當于一次設定 NewSize/MaxNewSIze,而且兩者相等,適用于生產環境。-Xmn 配合 -Xms/-Xmx,即可將堆內存布局完成。
-Xmn參數是在JDK 1.4 開始支持。
垃圾回收器選擇
JVM給出了3種選擇:串行收集器、并行收集器、并發收集器。串行收集器只適用于小數據量的情況,所以生產環境的選擇主要是并行收集器和并發收集器。
默認情況下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數。JDK5.0以后,JVM會根據當前系統配置進行智能判斷。
串行收集器
-XX:+UseSerialGC:設置串行收集器。
并行收集器(吞吐量優先)
-XX:+UseParallelGC:設置為并行收集器。此配置僅對年輕代有效。即年輕代使用并行收集,而年老代仍使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的線程數,即:同時有多少個線程一起進行垃圾回收。此值建議配置與CPU數目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0開始支持對年老代并行收集。
-XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間(單位毫秒)。如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此時間。
-XX:+UseAdaptiveSizePolicy:設置此選項后,并行收集器會自動調整年輕代Eden區大小和Survivor區大小的比例,以達成目標系統規定的最低響應時間或者收集頻率等指標。此參數建議在使用并行收集器時,一直打開。
并發收集器(響應時間優先)
-XX:+UseConcMarkSweepGC:即CMS收集,設置年老代為并發收集。CMS收集是JDK1.4后期版本開始引入的新GC算法。它的主要適合場景是對響應時間的重要性需求大于對吞吐量的需求,能夠承受垃圾回收線程和應用線程共享CPU資源,并且應用中存在比較多的長生命周期對象。CMS收集的目標是盡量減少應用的暫停時間,減少Full GC發生的幾率,利用和應用程序線程并發的垃圾回收線程來標記清除年老代內存。
-XX:+UseParNewGC:設置年輕代為并發收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此參數。
-XX:CMSFullGCsBeforeCompaction=0:由于并發收集器不對內存空間進行壓縮和整理,所以運行一段時間并行收集以后會產生內存碎片,內存使用效率降低。此參數設置運行0次Full GC后對內存空間進行壓縮和整理,即每次Full GC后立刻開始壓縮和整理內存。
-XX:+UseCMSCompactAtFullCollection:打開內存空間的壓縮和整理,在Full GC后執行。可能會影響性能,但可以消除內存碎片。
-XX:+CMSIncrementalMode:設置為增量收集模式。一般適用于單CPU情況。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代內存空間使用到70%時就開始執行CMS收集,以確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發生。
其它垃圾回收參數
-XX:+ScavengeBeforeFullGC:年輕代GC優于Full GC執行。
-XX:-DisableExplicitGC:不響應 System.gc() 代碼。
-XX:+UseThreadPriorities:啟用本地線程優先級API。即使 java.lang.Thread.setPriority() 生效,不啟用則無效。
-XX:SoftRefLRUPolicyMSPerMB=0:軟引用對象在最后一次被訪問后能存活0毫秒(JVM默認為1000毫秒)。
-XX:TargetSurvivorRatio=90:允許90%的Survivor區被占用(JVM默認為50%)。提高對于Survivor區的使用率。
輔助信息參數設置
-XX:-CITime:打印消耗在JIT編譯的時間。
-XX:ErrorFile=./hs_err_pid.log:保存錯誤日志或數據到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆內存時的路徑。
-XX:-HeapDumpOnOutOfMemoryError:當首次遭遇內存溢出時Dump出此時的堆內存。
-XX:OnError=";":出現致命ERROR后運行自定義命令。
-XX:OnOutOfMemoryError=";":當首次遭遇內存溢出時執行自定義命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆內存中類實例的柱狀信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印線程棧中并發鎖的相關信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:當一個方法被編譯時打印相關信息。
-XX:-PrintGC:每次GC時打印相關信息。
-XX:-PrintGCDetails:每次GC時打印詳細信息。
-XX:-PrintGCTimeStamps:打印每次GC的時間戳。
-XX:-TraceClassLoading:跟蹤類的加載信息。
-XX:-TraceClassLoadingPreorder:跟蹤被引用到的所有類的加載信息。
-XX:-TraceClassResolution:跟蹤常量池。
-XX:-TraceClassUnloading:跟蹤類的卸載信息。
關于參數名稱等
標準參數(-),所有JVM都必須支持這些參數的功能,而且向后兼容;例如:
-client——設置JVM使用Client模式,特點是啟動速度比較快,但運行時性能和內存管理效率不高,通常用于客戶端應用程序或開發調試;在32位環境下直接運行Java程序默認啟用該模式。
-server——設置JVM使Server模式,特點是啟動速度比較慢,但運行時性能和內存管理效率很高,適用于生產環境。在具有64位能力的JDK環境下默認啟用該模式。
非標準參數(-X),默認JVM實現這些參數的功能,但是并不保證所有JVM實現都滿足,且不保證向后兼容;
非穩定參數(-XX),此類參數各個JVM實現會有所不同,將來可能會不被支持,需要慎重使用;
這一篇是系列
文章的第三篇,前面兩篇分別談了
測試的必需性《關于
軟件測試的幾點反思 - 測試是必需的嗎?》,以及測試
工作的一些內容《關于軟件測試的幾點反思 - 測試工作的三個階段》,接下來想聊一下
測試團隊的組織。
要討論這個話題,首先要討論下測試人員本身的歸屬,因為通常是人多了才有組織的必要,很多東西都是一點點長出來的。
我在讀研期間實習的一家公司,根本沒有專職的測試人員,回頭想想當時還是挺大膽的,因為做的是比較核心的系統,而且當時像我這種實習生都寫了很多核心的涉及金額計算的代碼,然后大家自測下就上線了。這種情況也持續了好久,也驗證了不一定必需,在特定的情況下。
個人的工作經歷,沒有一開始去很小的研發組織。后面工作后的
面試中,也接觸過很多規模較小的公司的測試人員,這種情況下大部分是直接歸屬到項目,匯報給開發經理。人數少,大部分是比較基礎的
黑盒測試,相對也比較弱勢。沒有任何貶低的意思,但是客觀來說,這個階段的測試很難有一些比較深入的測試技術上的實踐,時間不允許,也處于沒有人帶的情況,大家基本上都專注在項目的
功能測試上面。一直覺得環境對人的影響是比較大的。有些比較有上進心的同學會自己學一些技術,但是因為沒有指導,也沒有實際應用的場景,通常比較淺。
后面等到整個研發體系發展大了之后,可能測試人員也慢慢多了起來,同時服務于多個開發小組,于是就出現了測試團隊的二級組織。比如對口每個開發小組的有幾個人,或者更多,然后有一個lead或者first line manager,然后這些人匯總到一個second line的manager。這個時候隨著測試團隊規模的壯大,當然也是隨著整個研發組織的壯大,以及業務和開發方面提出了更多更高的質量要求,測試團隊在客觀上有了進一步提升的需求。主觀上因為second line的出現,會不再滿足于完成基本的測試工作,也有提升的動力。一些測試的規范,
用例設計,
缺陷管理,數據的分析,工具平臺的引入,自動化的開展等慢慢開始引入。
接下來,可能到研發部門層面有一個完整的測試團隊,進而可能是整個公司,或者事業部(BG,BU)層面有一個完整的測試部門,或者中心。
目前看到的
騰訊和阿里的幾個大的業務導向的事業部都是這樣的情況,比如騰訊的
互聯網,互動娛樂,電商(曾經);阿里的淘寶,天貓和支付寶,都是在事業部層面有一個完整的測試團隊。
進一步,如果這類很大型的公司,把全集團的測試集中到一起的還沒看到,主要是因為組織架構的頂層還是按業務來劃分的,比如事業部制。
基于上面的討論,我們可以從測試的集中這種角度來看看測試團隊的情況,這樣劃分就有兩種,集中還是不集中。
集中的例子就是上面說的情況,所有專職測試人員都在一個小組,一個大的二級組,進而一個中心,一個部門,數據結構上是一顆樹。非集中的情況就是測試人員散落在不同的開發中心,或者開發組,是一個森林。每一塊的測試人員組成一個小組,匯報給開發中心的總監,或者開發經理。 目前了解到不少公司是這樣來組織的。
每一種組織方式都是結合了公司的實際情況和要求,但是如果單純從測試團隊的價值和效率方面,目前來看,會覺得集中到一起是個更好的方式,主要的理由是下面幾點。
1. 集中到一起之后,因為資源的整合,可以減少各個團隊的重復建設,集中來做一些平臺建設,技術研究,或者橫行的技術共享,有利于提升團隊的技術深度。
2. 從業務的角度,集中后測試可以橫向的對齊,來看各個項目的質量情況,研發流程的過程執行和效率的情況。從整個組織的角度,對研發的質量和效率有促進的作用。
3. 從測試人員個人發展的角度,因為整個測試組織有了更好的深度,個人發展的空間也會更大,無論技術還是管理方面。
4. 測試人員的歸屬感,有自己的部門和自己的職業發展通道。
談到和項目的對應,目前采用測試集中方面的團隊也基本上是矩陣式管理,特別是針對負責業務測試的小組。一方面,從組織架構上是歸屬于質量部或者質量中心,但是從日常工作上,是歸屬到項目或者產品,和對應的產品、開發團隊密切配合,包括座位可能都在一起。
就目前觀察的情況來看,這種組織架構相對是比較成熟,也比較普遍被采用的,運作起來也還比較順暢。
第二個方面,想討論一下測試人員的內部細分。IT行業早已經是內部就開始隔行如隔山,分得非常細了。考慮對口的產品和技術形態,測試人員也有很大的差異了,比如測試通信設備的,測試Web網站的,以及測試app的,其背景知識,工作流程也有很大的差異。即使不考慮這方面,從專注的測試工作內容上看,也有進一步的細分。我接觸到的會分為四個方面:
1. 業務測試
這一部分的測試人員就是上面提到的矩陣式管理的那一類,負責具體的產品的測試和質量提升。所以這一類的人就數量上來說通常是最多的。
2. 專項測試
如果測試組織稍大,對測試的深度有更高的要求,而有些測試類型又需要比較長時間的積累,且技能有橫向的共用性,那么就可能有專人來做,俗稱專項。比如安全測試,性能測試等。就實際運作的情況來看,像安全測試這種看起來確實比較有必要,因為沒有長時間專注的積累難以有效果。這樣的專項測試通常人數不是很多。
3. 質量管理
有些地方叫QA或者SQA,就是第二篇里面提到的專注在質量數據的收集,研發流程的管理和推動等事項上面的一些人。通常放在測試部門,但是也可能放在研發管理等團隊。
4. 測試開發
當整個測試的團隊比較大,需要很多共性的基礎的平臺和工具建設,就可能會抽出一個團隊專門來做這方面的事情,稱之為測試開發。
實際中,關于業務測試和測試開發,其實有時候界限是沒有那么清楚的,取決于多個方面的因素:
1. 老大觀念
沒辦法,這個很重要。接觸到幾位部門級測試團隊的負責人,觀念不完全一樣,有些認為獨立的測試開發團隊很重要,且愿意大的投入,有些覺得沒必要有專門的測試開發團隊,業務測試團隊應該有能力來做測試技術方面的事情。
2. 業務測試團隊的能力
這個要看實際的情況,業務測試如果要做得比較好,業務測試的團隊本身也需要比較好的技術能力,所以很多測試開發意義上的事情業務測試團隊也可以做,實際也在做。但是也有些情況,業務測試團隊本身的技術能力不夠,或者時間非常的局限。從業務測試團隊本身的意愿上,肯定也希望能做一些技術方面的事情。
3. 測試開發團隊和業務測試團隊的雙向互動的情況
一方面是看測試開發團隊做的東西能否在業務測試團隊落地,涉及方案本身,是否貼合項目時間,以及和業務測試團隊的配合的情況。這個實際情況應該還是比較復雜的,各個團隊面臨的實際情況都不太一樣。
最后想討論下關于測試人員外包。這里不討論整個項目的測試外包,那是另一個范疇,也曾經和一個資深的測試leader深入聊過,不過最終也只是些理論推導,沒有實際的應用,很多問題也不好說。
自己關于外包的觀念也一直在變,因為看到身邊不同的實際的例子,目前的想法大致如下:
1. 覺得外包非常的有幫助,能幫助完成很多的測試工作,在招聘速度上也很有優勢,所以是個不錯的選擇。
2. 就實際運作來看,覺得外包的比例不能太高,一個正式配1-2個外包應該效果還不錯,太多了對項目,對外包同學的成長覺得都不太好。
在我們團隊的應用中,還有幾個小的實例的體會
1. 像面試正式員工一樣面試外包
如果我們覺得外包只是過來做黑盒的手工測試,隨便找幾個人就好了,可能效果不會太好,因為最終還是取決于人。我們目前的幾位外包同學都是我們非常認真的面試過,從很多位中挑選出來的,在團隊里面發揮的作用其實已經超出了我們的預期。
2. 更放手讓他去做
就目前項目的情況,我們的外包同學除了做好基本的黑盒手工測試,他們也可以牽頭一個項目的測試跟進發周報,自動化的用例制作和問題定位,crash問題的跟進分析等,而且工作的態度和主動性非常好。因為我們沒有給他們設明確的界限,只要他愿意嘗試,也具備基本的能力,那就可以去做。這個和上面的1也有關系。
3. 關注外包同學的發展
這兩年接觸了很多外包同學,團隊里有好幾位正式同學也是之前做過外包,所以對外包的看法有些不同了,在職業發展上盡量和正式同學一樣看待,并關注他們個人的發展。
在這個充斥著企業文化,價值觀的年代,測試如果作為一個獨立的部門,也一定會被問這樣的問題:作為一個獨立的測試部門,我們的核心價值是什么?
這個問題好像一直都會在,值得去想想看。
如果你是位個人站長,就能理解網站速度的重要性。自從
Google 算法開始使用網頁加載時間作為搜索排序參數之后,網站速度對 SEO 的影響非常大。而且,很慢的加載速度會對網站訪問者產生消極的影響。如果你的網站加載速度很慢,需要等待一段時間才能加載,那么用戶很有可能不會再次訪問 這個網站。
所以,為了解決以上說到的問題,我們收集整理了一個最好的免費網站速度
測試和分析工具列表。接下來介紹的工具都是免費,而且會提供非常詳細的數據報告給用戶,幫助用戶做些必要的補救措施。希望大家都能在下面的列表中找到對自己有幫助的,使自己的網站訪問速度越來越快!
1. Google PageSpeed Insights
Google PageSpeed Insights 允許用戶分析網站頁面的內容,并且會提供加快網站訪問速度的建議。
2. GT Matrix
GTmetrix 可以幫助用戶開發一個快速,高效,能全面改善用戶體驗的網站。它會為網站性能打分,然后提供可行性的建議來改善已發現的問題。
3. Neustar Free Load Testing & Performance Test
neustar 這個工具是個簡單快速生成網站性能分析數據的工具。它能忽略掉大小和地理位置來檢測和負載測試網站,非常容易得出網站的性能分析,幫助用戶加快網站加載速度。
Web Page Analyzer 是個非常強大的速度
測試工具,提供詳細的網站分析數據并且會提供提高網站性能的建議。它提供大量的 web 頁面速度報告,global report,外部文件計算,加載時間,網站分析數據和改善建議。
5. Pingdom
Pingdom 是個非常杰出的工具,幫助用戶生成大量網站的報告(頁面大小,瀏覽器緩存,性能等級等),確定網站的加載時間,而且允許用戶跟蹤性能的歷史記錄,能在不同位置進行網站測試。
6. Load Impact
Load Impact 允許用戶做些 web 應用的負載和
性能測試。它不斷增加網站流量來測量網站性能。Load Impact 會選擇一個全球負載區,測試模擬客戶,帶寬,接收數據和每秒請求等。越來越多客戶變活躍,這個工具會用個漂亮的圖表來展示測量的加載時間。
7. WebPage Test
用戶可以使用 WebPage Test 來進行簡單的測試,又或者是進行高級的測試,比如多步事物處理,視頻采集,內容屏蔽等。測試結果會提供豐富的診斷信息,包括資源加載瀑布圖表,頁面速度優化檢測和改善建議等。
8. Octa Gate Site Timer
Octa Gate Site Timer 工具允許用戶檢測每個用戶加載一個或多個頁面的時間。當頁面加載的時候,SiteTimer 存儲每個項目加載的數據和用戶接收的數據,這些數據會用一個網格來顯示。
9. Which Loads Faster
Which Loads Faster 是用來測試 web 性能問題的工具,可以在每個用戶的瀏覽器測試。whichloadsfaster 是開源的,使用 HTML 和 JavaScript 編寫的測試工具,完全在客戶端運行。
10. Yslow
YSlow 能分析 web 頁面,基于一系列 web 頁面高性能規則提供改進網頁性能的建議。
11. Show Slow
Show Slow 是個開源測試工具,幫助檢測各種網站性能指標。它會把頁面速度的檢測結果排序,有 dynaTrace AJAX 版,WebPageTest 和 YSlow。它會用圖形化顯示排序結果,幫助用戶理解哪些因素會影響網站的性能。
12. Free Speed Test
Free Speed Test 是個在線網站速度測試工具,可以通過全球多個數據中心來檢測加載時間和網站速度。這可以讓用戶了解全球各個角落網站的實際加載速度。
①在從服務器可以執行查詢
工作(即我們常說的讀功能),降低主服務器壓力;
②在從主服務器進行備份,避免備份期間影響主服務器服務;
③當主服務器出現問題時,可以切換到從服務器。
所以我在項目部署和實施中經常會采用這種方案.
my.cnf配置文件 /etc/my.cnf
mysql數據庫位置 datadir=/var/lib/mysql
主數據庫:192.168.2.119
從數據庫:192.168.2.220
服務器類型: 虛擬機
+ mysql5.0.77 安裝:
① 配置好linux的yum服務后,直接yum -y install mysql即可
附:安裝php\mysql一條命令安裝:yum -y install httpd php mysql mysql-server php-mysql
② 啟動MySQL
service mysqld start(restart|stop)
一、設置主庫
1、修改主庫my.cnf,主要是設置個不一樣的id和logbin(#這部可依具體環境而定,壓力大的化可采用huge.cnf)
[root@localhost etc]#vi /etc/my.cnf
# 記住這部分一定要配置在[mysqld]后面,否則無法找到從節點,各個配置項的含義可自己查閱文檔
[mysqld] log-bin=mysql-bin server-id=1 binlog-ignore-db=information_schema binlog-ignore-db=cluster binlog-ignore-db=mysql |
2、啟動主庫生效
[root@localhost etc]service mysqld restart
3、登陸主庫
[root@localhost etc]mysql -u root -p
4、賦予從庫權限帳號,允許用戶在主庫上讀取日志
mysql> grant all privileges on *.* to '用戶名'@'%' identified by '密碼';
5、檢查創建是否成功
select user,host from mysql.user;
6、鎖主庫表
mysql> flush tables with read lock;7、顯示主庫信息
記錄File和Position,從庫設置將會用到
mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 98 | | | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) # 說明,如果執行這個步驟始終為Empty set(0.00 sec),那說明前面的my.cnf沒配置對。 |
8、另開一個終端登陸220,打包主庫遷移數據(如果你使用的yum安裝,有默認數據庫并未做任何改動,則不需要進行拷貝)
目的是為了保證兩臺服務器的mysql數據庫一致,這里可以自行tar打包或者使用mysqldump命令備份恢復的方式進行。
二、設置從庫
1、傳輸拿到主庫包、解包
# 登陸從庫
從上一步中備份的數據庫恢復到220服務器節點上。
2、在119節點上解鎖主庫表(對應第一點設置主庫中第6步鎖主庫表的操作)
mysql> unlock tables;
3、在220節點上修改從庫my.cnf(位置一樣)
# 記住這部分一定要配置在[mysqld]后面,否則無法找到從節點,各個配置項的含義可自己查閱文檔
[mysqld] log-bin=mysql-bin server-id=2 binlog-ignore-db=information_schema binlog-ignore-db=cluster binlog-ignore-db=mysql replicate-do-db=test replicate-ignore-db=mysql log-slave-updates slave-skip-errors=all slave-net-timeout=60 master-host=192.168.2.119 master-user=root master-password=pfingo |
4、在220節點上驗證連接主庫
[root@localhost etc]mysql -h 192.168.2.119 -u 用戶名 -p
5、在220節點從庫上設置同步
#設置連接MASTER MASTER_LOG_FILE為主庫的File,MASTER_LOG_POS為主庫的Position #注意下面第二條命令語句中的master_log_file='mysql-bin.000001', master_log_pos=98;對應為前面在主庫中執行的show master status;結果 mysql> slave stop; mysql> change master to master_host='192.168.2.119',master_user='root',master_password='pfingo',master_log_file='mysql-bin.000001', master_log_pos=98; mysql> slave start; |
6、啟動從庫服務
mysql> slave start;
7、進行測試
在主庫上的test庫上建立名為myTest的表
mysql> CREATE TABLE `myTest` ( `id` INT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT , `username` VARCHAR( 20 ) NOT NULL , `password` CHAR( 32 ) NOT NULL , `last_update` DATETIME NOT NULL , `number` FLOAT( 10 ) NOT NULL , `content` TEXT NOT NULL , PRIMARY KEY ( `id` ) ) ENGINE = MYISAM ; |
在從表中馬上看到了效果,主從同步成功了;
為了更進一步驗證在從庫上輸入show slave status\G;
mysql> show slave status\G;
Slave_IO_Running: Yes(網絡正常);
Slave_SQL_Running: Yes(表結構正常)
進一步驗證了以上過程的正確性。
一、注解基本了解和應用
1、何為注解?
注解就是一種標記,在程序中加了注解就等于加了標記,沒加,就沒有標記。
2、注解有何作用?
加了注解,
java編譯器、開發工具或是其他程序可以通過反射技術了解你的類或各種元素是否有標記,有什么標記就做什么
樣的事情。比如:子類重寫父類的方法,方法上必須有@override標記;若一個方法已過時不用了,就該方法添加注
解@Deprecated,調用者反射時就明白這方法已過時
3、注解在哪標記,也就是說能為哪些元素標記?
可以在包、類、字段、方法、局部變量
二、自定義注解及其應用
1、先定義個注解類,如下代碼:
/** * 自定義注解類 * @author Administrator * */ public @interface AnimTest { } |
2、將這個注解類應用到某個類上,然后用反射查看判斷該類是否被這個注解類所標記
package com.itcast.test; import com.itcast.zhujie.AnimTest; @AnimTest //這是自定義的注解 public class ZhujieTest { /** * @param args */ public static void main(String[] args) { boolean isAnim = ZhujieTest.class.isAnnotationPresent(AnimTest.class); if(isAnim) System.out.println("it has one"); else System.out.println("no have"); } } |
輸出的結果是:no have ;表示該類沒有找到注解標記,這是為何呢?不是在類上已經使用了注解了嘛?
回答這問題之前,我們先
學習一個東西,Retention元注釋類,指的是注釋類型的注釋要留多久。如果某個注釋類型沒有聲明Retention元注釋,則保留策略為默認的RetentionPolicy.CLASS,表示保留到編譯時,運行時就會被剔除。
RetentionPolicy 是一個枚舉類型,有三個值:SOURCE、CLASS 、 RUNTIME 分別對應著Java源文件、class文件、內存中的字節碼 我們在重新看下上個代碼,AnimTest 沒有聲明Retention,故保留默認的CLASS,只保留到編譯時期。所以內存中加載類文件時,注解類已被清除,所以才會找不到AnimTest注解類的標記。要想在反射中能找到該標記,只要設置下注解類的保留周期,所以接著改下這個注解類
/** * 自定義注解類 * @author Administrator * */ @Retention(value = RetentionPolicy.RUNTIME) //表示運行時也保留該注解類 public @interface AnimTest { } |
在執行代碼,結果是:it has one
3、現在有個問題,就是注解類的使用范圍是怎樣的呢?只能在類上么?
其實有個元注釋Tagert來限定范圍的。Tagert 指的是注釋類型所適用的程序元素的種類。如果注釋類沒有聲明Tagert元注釋,則可以適用于任何元素上,如果聲明了,編譯器就會強制實施指定的范圍
如果要使AnimTest只作用于方法上,則在注釋類聲明Tagert
/** * 自定義注解類 * @author Administrator * */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) //限定范圍,作用于方法 public @interface AnimTest { } |
那么這個注解類就不能作用于類上了,否則會編譯異常,只能作用于方法上,代碼如下:
public class ZhujieTest { /** * @param args */ @AnimTest //作用于方法上 public static void main(String[] args) { boolean isAnim = ZhujieTest.class.isAnnotationPresent(AnimTest.class); if(isAnim) System.out.println("it has one"); else System.out.println("no have"); } } |
如果注釋類既可以使用類上也可以作用 于方法上,只要修改為 @Target(value = {ElementType.METHOD,ElementType.TYPE}),type是表示類型
三、為注解增加各種屬性
1、現在給注解類添加各種屬性,類似接口形式,只提供方法
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.itcast.classdemo.WeekDay; /** * 自定義注解類 * @author Administrator * */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE}) public @interface AnimTest { String color(); //字符串 int count(); //整型 int[] arr(); //數組 MetaAnnotation meta() default @MetaAnnotation("very food"); //注釋類型 WeekDay day() default WeekDay.MON; //枚舉類型 } //注釋類 public @interface MetaAnnotation { String value() default "heollo"; //提供默認值 } //枚舉類 public enum WeekDay { SUN,MON } |
2、以下是測試注釋類調用屬性
import com.itcast.zhujie.AnimTest; @AnimTest (color ="red",count =1,arr=234) //這是自定義的注解,并為其屬性賦值 public class ZhujieTest { /** * @param args */ @AnimTest (color ="blue",count=2,arr={3,21}) public static void main(String[] args) { //是否含有AnimTest注釋標記 boolean isAnim = ZhujieTest.class.isAnnotationPresent(AnimTest.class); if(isAnim){ //獲取AnimTest注釋標記對象 AnimTest an = ZhujieTest.class.getAnnotation(AnimTest.class); //以下都是獲取AnimTest注釋標記對象的屬性 System.out.println(an.color()); // red System.out.println(an.count()); //1 System.out.println(Arrays.toString(an.arr())); //[234] System.out.println(an.meta().value()); //very good System.out.println(an.day()); //MON System.out.println("it has one"); }else System.out.println("no have"); } } |
經上述測試總結:
1)注釋類的屬性可以有String、int、數組、枚舉和注解
2)注釋類的屬性可以設置默認值,如String color() default "red";
3)在使用注釋類時,有屬性,但沒默認值,這時必須要賦值,否則編譯不通 過 @AnimTest (color ="blue",count=2,arr={3,21})
4)獲取類上的注釋對象,用反射技術AnimTest an = ZhujieTest.class.getAnnotation(AnimTest.class);
5)獲取注釋上的屬性,類似調用方法一樣,需要帶括號;
6)如果注釋類只有一個屬性要賦值,且屬性名為value,則賦值時屬性名和等號都可以省略,如:@MetaAnnotation("very food")