【性能優(yōu)化】納尼?內(nèi)存又溢出了?!是時候總結(jié)一波了??!
作者個人研發(fā)的在高并發(fā)場景下,提供的簡單、穩(wěn)定、可擴展的延遲消息隊列框架,具有精準的定時任務(wù)和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業(yè)提供了精準定時調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗。為使更多童鞋受益,現(xiàn)給出開源框架地址:https://github.com/sunshinelyz/mykit-delay
寫在前面
相信小伙伴們在平時工作的過程中,或多或少都會遇到一個場景:內(nèi)存溢出。如果你沒有遇到過這個場景,那就說明你是個假的程序員。哈哈,開個玩笑,平時工作過程中,我們確實會遇到這個問題。今天,我就將平時工作過程中遇到的內(nèi)存溢出情況做個簡單的總結(jié),以通俗易懂的代碼案例的形式直觀的分享給大家。希望能夠為小伙伴們帶來實質(zhì)性的幫助。
案例介紹
這里,我將在平時工作過程中總結(jié)的內(nèi)存溢出的情況,以代碼案例的形式直觀的分享給大家,希望能夠為小伙伴們帶來實質(zhì)性的幫助。
接下來,我們就以代碼案例的形式來分析各種內(nèi)存溢出的情況。
定義主類結(jié)構(gòu)
首先,我們創(chuàng)建一個類叫做BlowUpJVM,所有的案例實驗都是基于這個類進行。
- public class BlowUpJVM {
- }
棧深度溢出
- public static void testStackOverFlow(){
- BlowUpJVM.testStackOverFlow();
- }
棧不斷遞歸,而且沒有處理,所以虛擬機棧就不斷深入不斷深入,棧深度就這樣溢出了。
永久代內(nèi)存溢出
- public static void testPergemOutOfMemory1(){
- //方法一失敗
- List<String> list = new ArrayList<String>();
- while(true){
- list.add(UUID.randomUUID().toString().intern());
- }
- }
打算把String常量池堆滿,沒想到失敗了,JDK1.7后常量池放到了堆里,也能進行垃圾回收了。
然后換種方式,使用cglib,用Class把老年代取堆滿
- public static void testPergemOutOfMemory2(){
- try {
- while (true) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(OOM.class);
- enhancer.setUseCache(false);
- enhancer.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- return proxy.invokeSuper(obj, args);
- }
- });
- enhancer.create();
- }
- }
- catch (Exception e){
- e.printStackTrace();
- }
- }
虛擬機成功內(nèi)存溢出了,那JDK動態(tài)代理產(chǎn)生的類能不能溢出呢?
- public static void testPergemOutOfMemory3(){
- while(true){
- final OOM oom = new OOM();
- Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = method.invoke(oom, args);
- return result;
- }
- });
- }
- }
事實表明,JDK動態(tài)代理差生的類不會造成內(nèi)存溢出,原因是:JDK動態(tài)代理產(chǎn)生的類信息,不會放到永久代中,而是放在堆中。
本地方法棧溢出
- public static void testNativeMethodOutOfMemory(){
- int j = 0;
- while(true){
- Printer.println(j++);
- ExecutorService executors = Executors.newFixedThreadPool(50);
- int i=0;
- while(i++<10){
- executors.submit(new Runnable() {
- public void run() {
- }
- });
- }
- }
- }
這個的原理就是不斷創(chuàng)建線程池,而每個線程池都創(chuàng)建10個線程,這些線程池都是在本地方法區(qū)的,久而久之,本地方法區(qū)就溢出了。
JVM棧內(nèi)存溢出
- public static void testStackOutOfMemory(){
- while (true) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- while(true){
- }
- }
- });
- thread.start();
- }
- }
線程的創(chuàng)建會直接在JVM棧中創(chuàng)建,但是本例子中,沒看到內(nèi)存溢出,主機先掛了,不是JVM掛了,真的是主機掛了,無論在mac還是在windows,都掛了。
溫馨提示,這個真的會死機的。
堆溢出
- public static void testOutOfHeapMemory(){
- List<StringBuffer> list = new ArrayList<StringBuffer>();
- while(true){
- StringBuffer B = new StringBuffer();
- for(int i = 0 ; i < 10000 ; i++){
- B.append(i);
- }
- list.add(B);
- }
- }
不斷往堆中塞新增的StringBuffer對象,堆滿了就直接溢出了。
本文轉(zhuǎn)載自微信公眾號「冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系冰河技術(shù)公眾號。