自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于內(nèi)存安全問(wèn)題,你應(yīng)該了解的幾點(diǎn)!

存儲(chǔ) 存儲(chǔ)軟件
Java在內(nèi)存管理方面是要比C/C++更方便的,不需要為每一個(gè)對(duì)象編寫(xiě)釋放內(nèi)存的代碼,JVM虛擬機(jī)將為我們選擇合適的時(shí)間釋放內(nèi)存空間,使得程序不容易出現(xiàn)內(nèi)存泄漏和溢出的問(wèn)題

[[394720]]

前言

Java在內(nèi)存管理方面是要比C/C++更方便的,不需要為每一個(gè)對(duì)象編寫(xiě)釋放內(nèi)存的代碼,JVM虛擬機(jī)將為我們選擇合適的時(shí)間釋放內(nèi)存空間,使得程序不容易出現(xiàn)內(nèi)存泄漏和溢出的問(wèn)題

不過(guò),也正是因?yàn)镴ava把內(nèi)存控制的權(quán)利交給了Java虛擬機(jī),一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問(wèn)題,如果不了解虛擬機(jī)是怎么使用內(nèi)存的,那排查錯(cuò)誤將會(huì)成為一項(xiàng)異常艱難的工作

下面先看看JVM如何管理內(nèi)存的

內(nèi)存管理

根據(jù)Java虛擬機(jī)規(guī)范(第3版) 的規(guī)定,Java虛擬機(jī)所管理的內(nèi)存將會(huì)包括以下幾個(gè)運(yùn)行內(nèi)存數(shù)據(jù)區(qū)域:

  • 線(xiàn)程隔離數(shù)據(jù)區(qū):
    • 程序計(jì)數(shù)器: 當(dāng)前線(xiàn)程所執(zhí)行字節(jié)碼的行號(hào)指示器
    • 虛擬機(jī)棧: 里面的元素叫棧幀,存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等,方法被調(diào)用到執(zhí)行完成的過(guò)程對(duì)應(yīng)一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過(guò)程。
    • 本地方法棧: 和虛擬機(jī)棧的區(qū)別在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法,本地方法棧為虛擬機(jī)使用到的本地Native方法服務(wù)。
  • 線(xiàn)程共享數(shù)據(jù)區(qū):
    • 方法區(qū): 可以描述為堆的一個(gè)邏輯部分,或者說(shuō)使用永久代來(lái)實(shí)現(xiàn)方法區(qū)。存儲(chǔ)已被虛擬機(jī)加載的類(lèi)信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
    • 堆: 唯一目的就是存放對(duì)象的實(shí)例,是垃圾回收管理器的主要區(qū)域,分為Eden、From/To Survivor空間。

Java各版本內(nèi)存管理改進(jìn)

下圖中永久代理解為堆的邏輯區(qū)域,移除永久代的工作從JDK7就已經(jīng)開(kāi)始了,部分永久代中的數(shù)據(jù)(常量池)在JDK7中就已經(jīng)轉(zhuǎn)移到了堆中,JDK8中直接去除了永久代,方法區(qū)中的數(shù)據(jù)大部分被移到堆里面,還剩下一些元數(shù)據(jù)被保存在元空間里

內(nèi)存溢出

  • 內(nèi)存泄露Memory Leak: 申請(qǐng)的內(nèi)存空間沒(méi)有及時(shí)釋放,導(dǎo)致后續(xù)程序里這塊內(nèi)容永遠(yuǎn)被占用。
  • 內(nèi)存溢出Out Of Memory: 要求的內(nèi)存超過(guò)了系統(tǒng)所能提供的

運(yùn)行時(shí)數(shù)據(jù)區(qū)域的常見(jiàn)異常

在JVM中,除了程序計(jì)數(shù)器外,虛擬機(jī)內(nèi)存的其他幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域都有發(fā)生OOM異常的可能。

堆內(nèi)存溢出

不斷的創(chuàng)建對(duì)象,并且保證GC Roots到對(duì)象之間有可達(dá)路徑來(lái)避免垃圾回收機(jī)制清除這些對(duì)象。

  1. public class HeapOOM { 
  2.     static class ObjectInHeap{ 
  3.     } 
  4.     public static void main(String[] args) { 
  5.         List<ObjectInHeap> list = new ArrayList(); 
  6.         while (true) { 
  7.             list.add(new ObjectInHeap()); 
  8.         } 
  9.     } 

棧溢出

單個(gè)線(xiàn)程下不斷擴(kuò)大棧的深度引起棧溢出。

  1. public class StackSOF { 
  2.     private int stackLength = 1; 
  3.     public void stackLeak() { 
  4.         stackLength++; 
  5.         stackLeak(); 
  6.     } 
  7.     public static void main(String[] args) { 
  8.         StackSOF sof = new StackSOF(); 
  9.         try { 
  10.             sof.stackLeak(); 
  11.         } catch (Throwable e) { 
  12.             System.out.println("Stack Length: " + sof.stackLength); 
  13.             throw e; 
  14.         } 
  15.     } 

循環(huán)的創(chuàng)建線(xiàn)程,達(dá)到最大棧容量。

  1. public class StackOOM { 
  2.     private void dontStop() { 
  3.         while (true) { 
  4.         } 
  5.     } 
  6.     public void stackLeadByThread() { 
  7.         while (true) { 
  8.             Thread thread = new Thread(new Runnable() { 
  9.                 @Override 
  10.                 public void run() { 
  11.                     dontStop(); 
  12.                 } 
  13.             }); 
  14.             thread.start(); 
  15.         } 
  16.     } 
  17.     public static void main(String[] args) { 
  18.         StackOOM stackOOM = new StackOOM(); 
  19.         stackOOM.stackLeadByThread(); 
  20.     } 

運(yùn)行時(shí)常量池溢出

不斷的在常量池中新建String,并且保持引用不釋放。

  1. public class RuntimeConstantPoolOOM { 
  2.     public static void main(String[] args) { 
  3.         // 使用List保持著常量池的引用,避免Full GC回收常量池 
  4.         List<String> list = new ArrayList<String>(); 
  5.         int i = 0; 
  6.         while (true) { 
  7.             // intern()方法使String放入常量池 
  8.             list.add(String.valueOf(i++).intern()); 
  9.         } 
  10.     } 

方法區(qū)溢出

借助CGLib直接操作字節(jié)碼運(yùn)行時(shí)產(chǎn)生大量的動(dòng)態(tài)類(lèi),最終撐爆內(nèi)存導(dǎo)致方法區(qū)溢出。

  1. public class MethodAreaOOM { 
  2.     static class ObjectInMethod { 
  3.     } 
  4.     public static void main(final String[] args) { 
  5.         // 借助CGLib實(shí)現(xiàn) 
  6.         while (true) { 
  7.             Enhancer enhancer = new Enhancer(); 
  8.             enhancer.setSuperclass(ObjectInMethod.class); 
  9.             enhancer.setUseCache(false); 
  10.             enhancer.setCallback(new MethodInterceptor() { 
  11.                 @Override 
  12.                 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
  13.                     return methodProxy.invokeSuper(o, objects); 
  14.                 } 
  15.             }); 
  16.             enhancer.create(); 
  17.         } 
  18.     } 

元空間溢出

助CG Lib運(yùn)行時(shí)產(chǎn)生大量動(dòng)態(tài)類(lèi),唯一的區(qū)別在于運(yùn)行環(huán)境修改為Java 1.8,設(shè)置-XX:MaxMetaspaceSize參數(shù),便可以收獲java.lang.OutOfMemoryError: Metaspace這一報(bào)錯(cuò)

本機(jī)直接內(nèi)存溢出

直接申請(qǐng)分配內(nèi)存(實(shí)際上并沒(méi)有真正向操作系統(tǒng)申請(qǐng)分配內(nèi)存,而是通過(guò)計(jì)算得知內(nèi)存無(wú)法分配,于是拋出異常)

  1. public class DirectMemoryOOM { 
  2.     private static final int _1MB = 1024 * 1024; 
  3.     public static void main(String[] args) throws IllegalAccessException { 
  4.         Field unsafeField = Unsafe.class.getDeclaredFields()[0]; 
  5.         unsafeField.setAccessible(true); 
  6.         Unsafe unsafe = (Unsafe) unsafeField.get(null); 
  7.         while (true) { 
  8.             unsafe.allocateMemory(_1MB); 
  9.         } 
  10.     } 

常見(jiàn)案例

在工作中一般會(huì)遇到有以下幾種情況導(dǎo)致內(nèi)存問(wèn)題

傳輸數(shù)據(jù)量過(guò)大

因?yàn)閭鬏敂?shù)量過(guò)大、或一些極端情況導(dǎo)致代碼中間結(jié)果對(duì)象數(shù)據(jù)量過(guò)大,過(guò)大的數(shù)據(jù)量撐爆內(nèi)存

查詢(xún)出大量對(duì)象

這個(gè)多為SQL語(yǔ)句設(shè)置問(wèn)題,SQL未設(shè)置分頁(yè),用戶(hù)一次查詢(xún)數(shù)據(jù)量過(guò)大、頻繁查詢(xún)SQL導(dǎo)致內(nèi)存堆積、或是未作判空處理導(dǎo)致WHERE條件為空查詢(xún)出超大數(shù)據(jù)量等

接口性能問(wèn)題導(dǎo)致

這類(lèi)為外部接口性能較慢,占用內(nèi)存較大,并且短時(shí)間內(nèi)高QPS導(dǎo)致的,導(dǎo)致服務(wù)內(nèi)存不足,線(xiàn)程堆積或掛起進(jìn)而出現(xiàn)FullGC

元空間問(wèn)題

使用了大量的反射代碼,Java字節(jié)碼存取器生成的類(lèi)不斷生成

問(wèn)題排查

使用jmap分析內(nèi)存泄漏

1.生成dump文件

  1. jmap -dump:format=b,file=/xx/xx/xx.hprof pid 

2.dump文件下載到本地

3.dump文件分析

可以使用MAT,MAT可作為Eclipse插件或一個(gè)獨(dú)立軟件使用,MAT是一個(gè)高性能、具備豐富功能的Java堆內(nèi)存分析工具,主要用來(lái)排查內(nèi)存泄漏和內(nèi)存浪費(fèi)的問(wèn)題。

使用MAT打開(kāi)上一部后綴名.hprof的dump文件

  • Histogram:直方圖,各個(gè)類(lèi)的實(shí)例,包括個(gè)數(shù)和大小,可以查看類(lèi)引用和被引用的路徑。
  • Dominator Tree:支配圖,列出所有線(xiàn)程和線(xiàn)程下面的那些對(duì)象占用的空間。
  • Top Consumers:通過(guò)圖形列出消耗內(nèi)存多的實(shí)例。
  • Leak Suspects:MAT自動(dòng)分析的內(nèi)存泄漏報(bào)表

可以用這個(gè)工具分析出什么對(duì)象什么線(xiàn)程占用內(nèi)存空間較大,對(duì)象是被什么引用的,線(xiàn)程內(nèi)有哪些資源占用很高

以運(yùn)行時(shí)常量池溢出為例

打開(kāi)Histogram類(lèi)實(shí)例表

Objects是類(lèi)的對(duì)象的數(shù)量;Shallow是對(duì)象本身占用內(nèi)存大小、不包含其他引用;

Retained是對(duì)象自己的Shallow加上直接或間接訪(fǎng)問(wèn)到對(duì)象的Shallow之和,也可以說(shuō)是GC之后可以回收的內(nèi)存總和

從圖中可以看出運(yùn)行時(shí)常量池溢出的情況,產(chǎn)生了大量的String和char[]實(shí)例

在char[]上右鍵可以得到上圖所有char[]對(duì)象的被引用路徑,可以看出這些char數(shù)組都是以String的形式存在ArrayList中,并且是由main這個(gè)線(xiàn)程運(yùn)行的

可以看出是main線(xiàn)程中新建了一個(gè)數(shù)組,其中存了32w+個(gè)長(zhǎng)度為6的char數(shù)組組成的String造成的內(nèi)存溢出

 

關(guān)于MAT的詳細(xì)使用可以從MAT官方教程學(xué)習(xí)更多

 

責(zé)任編輯:武曉燕 來(lái)源: 月伴飛魚(yú)
相關(guān)推薦

2020-10-29 10:26:28

DevOps軟件自動(dòng)化

2024-09-02 14:24:13

2023-09-02 21:31:16

Java內(nèi)存泄漏

2024-02-21 23:11:19

2020-04-28 18:20:04

Ubuntu 20.0UbuntuLinux

2012-01-16 10:41:25

安全互聯(lián)網(wǎng)IT部門(mén)

2011-03-29 10:41:51

Java線(xiàn)程安全

2018-08-23 08:21:54

TensorFlow機(jī)器學(xué)習(xí)人工智能

2011-07-14 14:21:11

2024-07-30 13:48:37

2013-09-17 09:35:15

云存儲(chǔ)

2018-06-11 11:03:09

2020-02-27 09:39:42

云安全云計(jì)算網(wǎng)絡(luò)安全

2011-07-18 08:58:24

2019-02-13 15:49:00

2015-03-20 09:22:01

網(wǎng)絡(luò)安全授權(quán)用戶(hù)身份訪(fǎng)問(wèn)管理

2017-04-07 16:30:51

Androidstrings.xml原則

2015-07-15 16:53:55

IP游戲基礎(chǔ)知識(shí)

2020-11-15 23:29:01

大數(shù)據(jù)安全數(shù)據(jù)安全網(wǎng)絡(luò)攻擊

2018-03-22 16:32:49

大數(shù)據(jù)數(shù)據(jù)集數(shù)據(jù)處理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)