日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

调查内存泄漏第1部分–编写泄漏代码

發(fā)布時(shí)間:2023/12/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 调查内存泄漏第1部分–编写泄漏代码 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前幾天,我發(fā)現(xiàn)了這個(gè)小問題:該服務(wù)器運(yùn)行了一段時(shí)間,然后掉下來了。 然后通過啟動(dòng)腳本重新啟動(dòng),整個(gè)過程重復(fù)進(jìn)行。 這聽起來并不那么糟糕,盡管對數(shù)據(jù)的損失很大,但對業(yè)務(wù)的重要性并不重要,因此我決定仔細(xì)研究一下,找出問題出在哪里。 首先要注意的是,服務(wù)器通過了所有的單元測試和大量的集成測試。 它在使用測試數(shù)據(jù)的所有測試環(huán)境中都能很好地運(yùn)行,那么生產(chǎn)中出了什么問題? 很容易猜到,在生產(chǎn)中,它的負(fù)載可能比測試重,或者比設(shè)計(jì)所允許的負(fù)載大,因此它用盡了資源,但是什么資源?在哪里? 這是一個(gè)棘手的問題。

為了演示如何研究此問題,首先要做的是編寫一些泄漏的示例代碼,而我將使用Producer Consumer模式來執(zhí)行此操作,因?yàn)槲铱梢匝菔舅拇髥栴}。

為了演示泄漏的代碼1,我需要像往常一樣需要一個(gè)高度人為的方案,在這種情況下,您可以想象您在一個(gè)將股票銷售量記錄在數(shù)據(jù)庫中的系統(tǒng)上的股票經(jīng)紀(jì)人工作。 訂單由一個(gè)簡單的線程接收并放入隊(duì)列中。 然后,另一個(gè)線程從隊(duì)列中獲取訂單,并將其寫入數(shù)據(jù)庫。 的
Order POJO非常簡單,如下所示:

public class Order { private final int id; private final String code; private final int amount; private final double price; private final long time; private final long[] padding; /** * @param id *??????????? The order id * @param code *??????????? The stock code * @param amount *??????????? the number of shares * @param price *??????????? the price of the share * @param time *??????????? the transaction time */ public Order(int id, String code, int amount, double price, long time) { super(); this.id = id; this.code = code; this.amount = amount; this.price = price; this.time = time; // This just makes the Order object bigger so that // the example runs out of heap more quickly. this.padding = new long[3000]; Arrays.fill(padding, 0, padding.length - 1, -2); } public int getId() { return id; } public String getCode() { return code; } public int getAmount() { return amount; } public double getPrice() { return price; } public long getTime() { return time; } }

Order POJO是一個(gè)簡單的Spring應(yīng)用程序的一部分,該應(yīng)用程序具有三個(gè)關(guān)鍵抽象,當(dāng)Spring調(diào)用它們的start()方法時(shí),它們會(huì)創(chuàng)建一個(gè)新線程。

其中第一個(gè)是OrderFeed 。 它的run()方法創(chuàng)建一個(gè)新的虛擬訂單并將其放置在隊(duì)列中。 然后,它會(huì)休眠一會(huì)兒,然后再創(chuàng)建下一個(gè)訂單。

public class OrderFeed implements Runnable { private static Random rand = new Random(); private static int id = 0; private final BlockingQueue<Order> orderQueue; public OrderFeed(BlockingQueue<Order> orderQueue) { this.orderQueue = orderQueue; } /** * Called by Spring after loading the context. Start producing orders */ public void start() { Thread thread = new Thread(this, "Order producer"); thread.start(); } /** The main run loop */ @Override public void run() { while (true) { Order order = createOrder(); orderQueue.add(order); sleep(); } } private Order createOrder() { final String[] stocks = { "BLND.L", "DGE.L", "MKS.L", "PSON.L", "RIO.L", "PRU.L", "LSE.L", "WMH.L" }; int next = rand.nextInt(stocks.length); long now = System.currentTimeMillis(); Order order = new Order(++id, stocks[next], next * 100, next * 10, now); return order; } private void sleep() { try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }

第二類是OrderRecord ,它負(fù)責(zé)從隊(duì)列中獲取訂單并將其寫入數(shù)據(jù)庫。 問題在于將訂單寫入數(shù)據(jù)庫要花費(fèi)的時(shí)間要長得多。 我的recordOrder(…)方法中有1秒的長時(shí)間睡眠,這證明了這一點(diǎn)。

public class OrderRecord implements Runnable { private final BlockingQueue<Order> orderQueue; public OrderRecord(BlockingQueue<Order> orderQueue) { this.orderQueue = orderQueue; } public void start() { Thread thread = new Thread(this, "Order Recorder"); thread.start(); } @Override public void run() { while (true) { try { Order order = orderQueue.take(); recordOrder(order); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * Record the order in the database * * This is a dummy method * * @param order *??????????? The order * @throws InterruptedException */ public void recordOrder(Order order) throws InterruptedException { TimeUnit.SECONDS.sleep(1); } }

結(jié)果很明顯: OrderRecord線程無法跟上,隊(duì)列將越來越長,直到JVM用完堆空間并OrderRecord為止。 這是生產(chǎn)者-消費(fèi)者模式的最大問題:消費(fèi)者必須能夠跟上生產(chǎn)者的步伐。

為了證明他的觀點(diǎn),我添加了第三類OrderMonitor ,該類每隔幾秒鐘打印一次隊(duì)列大小,以便您可以看到出現(xiàn)問題的地方。

public class OrderQueueMonitor implements Runnable { private final BlockingQueue<Order> orderQueue; public OrderQueueMonitor(BlockingQueue<Order> orderQueue) { this.orderQueue = orderQueue; } public void start() { Thread thread = new Thread(this, "Order Queue Monitor"); thread.start(); } @Override public void run() { while (true) { try { TimeUnit.SECONDS.sleep(2); int size = orderQueue.size(); System.out.println("Queue size is:" + size); } catch (InterruptedException e) { e.printStackTrace(); } } } }

為了完成陣容,我在下面添加了Spring上下文:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd" default-init-method="start" default-destroy-method="destroy"><bean id="theQueue" class="java.util.concurrent.LinkedBlockingQueue"/><bean id="orderProducer" class="com.captaindebug.producerconsumer.problem.OrderRecord"><constructor-arg ref="theQueue"/></bean><bean id="OrderRecorder" class="com.captaindebug.producerconsumer.problem.OrderFeed"><constructor-arg ref="theQueue"/></bean><bean id="QueueMonitor" class="com.captaindebug.producerconsumer.problem.OrderQueueMonitor"><constructor-arg ref="theQueue"/></bean></beans>

下一步是啟動(dòng)泄漏的示例代碼。 您可以通過轉(zhuǎn)到以下目錄來執(zhí)行此操作

/<your-path>/git/captaindebug/producer-consumer/target/classes

…然后鍵入以下命令:

java -cp /path-to/spring-beans-3.2.3.RELEASE.jar:/path-to/spring-context-3.2.3.RELEASE.jar:/path-to/spring-core-3.2.3.RELEASE.jar:/path-to/slf4j-api-1.6.1-javadoc.jar:/path-to/commons-logging-1.1.1.jar:/path-to/spring-expression-3.2.3.RELEASE.jar:. com.captaindebug.producerconsumer.problem.Main

…其中“ path-to ”是您的jar文件的路徑

有一兩件事,我真的很討厭關(guān)于Java的是,事實(shí)上,它是如此難以運(yùn)行在命令行中的任何程序。 您必須弄清楚什么是類路徑,需要設(shè)置哪些選項(xiàng)和屬性以及什么是主類。 當(dāng)然,肯定有可能想到一種簡單地鍵入Java programName的方法,并且JVM找出所有內(nèi)容在哪里,特別是如果我們開始使用約定而不是配置:它有多難?

您還可以通過附加一個(gè)簡單的jconsole來監(jiān)視泄漏的應(yīng)用程序。 如果要遠(yuǎn)程運(yùn)行它,則需要在上面的命令行中添加以下選項(xiàng)(選擇您自己的端口號(hào)):

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9010 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

…如果您查看使用的堆數(shù)量,您會(huì)發(fā)現(xiàn)隨著隊(duì)列變大,堆逐漸增加。

如果一千字節(jié)的內(nèi)存泄漏了,那么您可能永遠(yuǎn)也找不到它。 如果一千兆字節(jié)的內(nèi)存泄漏,問題將很明顯。 因此,目前要做的只是坐下來等待一些內(nèi)存泄漏,然后再繼續(xù)進(jìn)行下一步調(diào)查。 下次再說…

1源代碼可以在我在GitHub上的Producer Consumer項(xiàng)目中找到 。

參考: 調(diào)查內(nèi)存泄漏第1部分–在Captain Debug的Blog博客上,由JCG合作伙伴 Roger Hughes 編寫泄漏代碼 。

翻譯自: https://www.javacodegeeks.com/2013/12/investigating-memory-leaks-part-1-writing-leaky-code.html

總結(jié)

以上是生活随笔為你收集整理的调查内存泄漏第1部分–编写泄漏代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。