<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>内存泄漏 &#8211; 编码无悔 /  Intent &amp; Focused</title>
	<atom:link href="https://www.codelast.com/tag/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.codelast.com</link>
	<description>最优化之路</description>
	<lastBuildDate>Sun, 03 May 2020 12:33:52 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>[原创]（翻译）什么是Java的永久代（PermGen）内存泄漏</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b%ef%bc%88%e7%bf%bb%e8%af%91%ef%bc%89%e4%bb%80%e4%b9%88%e6%98%afjava%e7%9a%84%e6%b0%b8%e4%b9%85%e4%bb%a3%ef%bc%88permgen%ef%bc%89%e5%86%85%e5%ad%98%e6%b3%84%e6%bc%8f/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b%ef%bc%88%e7%bf%bb%e8%af%91%ef%bc%89%e4%bb%80%e4%b9%88%e6%98%afjava%e7%9a%84%e6%b0%b8%e4%b9%85%e4%bb%a3%ef%bc%88permgen%ef%bc%89%e5%86%85%e5%ad%98%e6%b3%84%e6%bc%8f/#respond</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Tue, 01 Oct 2013 08:30:14 +0000</pubDate>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[综合]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[java.lang.OutOfMemoryError: PermGen space]]></category>
		<category><![CDATA[memory leak]]></category>
		<category><![CDATA[PermGen]]></category>
		<category><![CDATA[内存泄漏]]></category>
		<category><![CDATA[永久代]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=7248</guid>

					<description><![CDATA[<p>
本文是我对这篇文章的翻译：<a href="http://plumbr.eu/blog/what-is-a-permgen-leak" target="_blank" rel="noopener noreferrer"><span style="background-color:#add8e6;">What is a PermGen leak?</span></a>&#160;为了便于阅读，我将原文附于此处，翻译穿插在其中。此外，为了防止原链接在未来某一天失效后，文中的图片再也看不到的问题，我将原文中的图片也保存到了本站的服务器上，我不知道原作者是否允许这样做，但我翻译本文仅在于传播知识的目的，在此向原作者表示深深的感谢：感谢你们的分享。<br />
<span id="more-7248"></span><br />
<span style="color: rgb(60, 58, 55); font-family: Helvetica, Arial, sans-serif; font-size: 24px; line-height: 26px; text-transform: uppercase;">WHAT IS A PERMGEN LEAK?</span><br />
<span style="font-size:14px;"><span style="font-family: 宋体;"><span style="color: rgb(0, 0, 255);">什么是Java的永久代（PermGen）内存泄漏？</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	What follows is a practical introduction to a specific type of&#160;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">memory</i>&#160;problems in Java applications. Namely &#8211; we will analyze the errors that cause the&#160;<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;"><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.OutOfMemoryError: PermGen space</i></b>symptom in the stack trace.&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b%ef%bc%88%e7%bf%bb%e8%af%91%ef%bc%89%e4%bb%80%e4%b9%88%e6%98%afjava%e7%9a%84%e6%b0%b8%e4%b9%85%e4%bb%a3%ef%bc%88permgen%ef%bc%89%e5%86%85%e5%ad%98%e6%b3%84%e6%bc%8f/" class="read-more">Read More </a></p>]]></description>
										<content:encoded><![CDATA[<p>
本文是我对这篇文章的翻译：<a href="http://plumbr.eu/blog/what-is-a-permgen-leak" target="_blank" rel="noopener noreferrer"><span style="background-color:#add8e6;">What is a PermGen leak?</span></a>&nbsp;为了便于阅读，我将原文附于此处，翻译穿插在其中。此外，为了防止原链接在未来某一天失效后，文中的图片再也看不到的问题，我将原文中的图片也保存到了本站的服务器上，我不知道原作者是否允许这样做，但我翻译本文仅在于传播知识的目的，在此向原作者表示深深的感谢：感谢你们的分享。<br />
<span id="more-7248"></span><br />
<span style="color: rgb(60, 58, 55); font-family: Helvetica, Arial, sans-serif; font-size: 24px; line-height: 26px; text-transform: uppercase;">WHAT IS A PERMGEN LEAK?</span><br />
<span style="font-size:14px;"><span style="font-family: 宋体;"><span style="color: rgb(0, 0, 255);">什么是Java的永久代（PermGen）内存泄漏？</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	What follows is a practical introduction to a specific type of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">memory</i>&nbsp;problems in Java applications. Namely &ndash; we will analyze the errors that cause the&nbsp;<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;"><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.OutOfMemoryError: PermGen space</i></b>symptom in the stack trace.<br />
	<span style="font-family:宋体;"><span style="color: rgb(0, 0, 255);"><span style="font-size: 14px;">本文实用性地介绍了Java程序的一个特殊的内存问题，即&mdash;&mdash;我们将分析stack trace中产生java.lang.OutOfMemoryError: PermGen space错误的原因。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	First of all we will go through the&nbsp;<a href="http://plumbr.eu/blog/what-is-a-permgen-leak#intro" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">core concepts</a>&nbsp;required to understand the subject &ndash; and explain what objects, classes, classloaders and the JVM memory model are. If you are familiar with the basic concepts, you can jump directly to&nbsp;<a href="http://plumbr.eu/blog/what-is-a-permgen-leak#samples" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">the next section</a>&nbsp;where I will describe two typical cases for the error in question alongside with hints and suggestions for solving it.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">首先，我们来看看理解该主题所需的<a href="http://plumbr.eu/blog/what-is-a-permgen-leak#intro" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">核心概念</span></a>，并且解释一下什么是对象（objects），类（classes），类加载器（classloaders）以及JVM内存模型（JVM memory model）。如果你已经熟知了这些基础概念，可以直接跳到下一节，我在它里面讨论了两种产生该错误的典型原因，以及解决此问题的提示和建议。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	Objects, Classes and ClassLoaders</h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">对象（Objects），类（Classes）以及类加载器（ClassLoaders）</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Well, I will not start with the very basics. I guess if you have already found us, you should be familiar with the concept that everything in Java is an&nbsp;<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">Object</b>. And that all Objects are specified by their&nbsp;<span style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline; font-weight: bold;">Class</span>. So every object has a reference to an instance of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.Class</i>&nbsp;describing the structure of that object&rsquo;s class.</p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	But what actually happens under the hood, when you create a new object in your code? For example if you write something truly complicated like</p>
<div>
	<span style="font-size:14px;"><span style="font-family: 宋体;"><span style="color: rgb(0, 0, 255);">我不会从最基础的概念开始讲。我假设你已经知道了我们，你应该熟知：在Java中一切皆是对象（Object），并且所有对象都是由它们的类（Class）指定的。所以每一个对象都有一个到java.lang.Class（用于描述对象的结构）的实例的引用。</span></span></span></div>
<div>
	<span style="font-size:14px;"><span style="font-family: 宋体;"><span style="color: rgb(0, 0, 255);">但是当你在代码中创建一个新对象的时候，到底发生了什么呢？例如，当你写下如下代码的时候：</span></span></span></div>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	&nbsp;</p>
<pre class="brush:java;first-line:1;pad-line-numbers:true;highlight:null;collapse:false;">
Person boss = new Person();
</pre>
<p>
<span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">The Java Virtual Machine (JVM) needs to understand the structure of the object to create. To achieve this, the JVM looks for the class called Person. And if the&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">Person</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">&nbsp;class is accessed for the first time during this particular execution of the program, it has to be&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">loaded</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">&nbsp;by the JVM, normally from the corresponding&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">Person.class</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">&nbsp;file. The process of seeking for the&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">Person.class&nbsp;</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">file on the drive, loading it into memory and parsing it&rsquo;s structure is called&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">class loading</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">. Ensuring proper class loading process is the responsibility of a&nbsp;</span><b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;"><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">ClassLoader</i></b><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">.&nbsp;</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">ClassLoaders are instances of&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">java.lang.ClassLoader</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">&nbsp;class and each and every class in a Java program has to be loaded by some&nbsp;</span><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; line-height: 18px;">ClassLoader.&nbsp;</i><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">As a result we now have the following relationships:</span><br />
<span style="font-family:宋体;"><span style="font-size: 14px;"><span style="color: rgb(0, 0, 255);">Java虚拟机（JVM）需要理解待创建对象的结构，为此，JVM会查找叫Person的类。并且，如果在程序的这一次运行中，Person类是第一次被访问的话，它必须要被JVM加载（loaded），通常这是从对应的Person.class文件加载的。从磁盘上查找Person.class文件、将其加载到内存中，并解析它的结构的过程，就叫作类的加载（class loading）。保证合适的类被加载是类加载器（ClassLoader）的职责。ClassLoader是java.lang.ClassLoader类的实例，并且Java程序中的每一个类都必须被一些ClassLoader加载。最后，我们可以得到下面的关系图：</span></span></span></p>
<p><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;"> <img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2013/10/permgen_leak_1.png" style="width: 570px; height: 72px;" /></span></p>
<p><span style="font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px; color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" style="font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;">As you can see from the next diagram every classloader holds references to all the classes it has loaded. For the purpose of our article these relationships are very interesting.</span><br />
<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">从下一幅图你可以看到，每一个classloader都持有它所加载的所有类的引用。由于本文的主旨的原因，这些关系非常有趣。</span></span></span></p>
<p><span style="color: rgb(60, 58, 55); font-family: Helverica, Arial, sans-serif; font-size: 13px; line-height: 18px;"> <img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2013/10/permgen_leak_2.png" style="width: 570px; height: 262px;" /></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Remember this image, we will need it later.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">记住这幅图，我们稍后会用到它。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	Permanent Generation</h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">永久代</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Almost every JVM nowadays uses a separate region of memory, called the Permanent Generation (or<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">PermGen</b>&nbsp;for short), to hold internal representations of java classes. PermGen is also used to store more information &ndash; find out the details from&nbsp;<a href="https://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">this post</a>&nbsp;if you are interested &ndash; but for our article it is safe to assume that only the class definitions are being stored in PermGen. The default size of this region on my two machines running java 1.6 is not a very impressive 82MB.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">当今，几乎所有JVM都使用一个称为永久代（Permanent Generation，或者简写为PermGen）的独立内存区域来保存Java类的内在表征。PermGen也用来保存更多信息&mdash;&mdash;如果感兴趣的话，你可以看看<a href="http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">这篇</span></a>文章&mdash;&mdash;但是对本文来说，我们可以假定PermGen只保存类的定义。对我那运行Java 1.6的机器来说，此内存区域的默认大小是82MB，一个很普通的数值。</span></span></span></p>
<p>	<img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2013/10/permgen_leak_3.png" style="width: 595px; height: 298px;" /><br />
	&nbsp;</p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	As I have explained in one of my earlier posts, a&nbsp;<a href="http://plumbr.eu/blog/what-is-a-memory-leak" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">memory leak</a>&nbsp;in Java is a situation where some objects are no longer used by an application, but the&nbsp;<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">Garbage Collector</b>&nbsp;fails to recognize them as unused. This leads to the&nbsp;<b style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;"><i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">OutOfMemoryError</i></b>&nbsp;if those unused objects contribute to the heap usage significantly enough that the next memory allocation request by the application cannot be fulfilled.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">就像我在以前一篇文章中解释的那样，Java的<a href="http://plumbr.eu/blog/what-is-a-memory-leak" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">内存泄漏</span></a>是指某些对象不再被应用程序使用，而垃圾收集器（Garbage Collector）却没能识别它们是&ldquo;不再使用的&rdquo;。如果那些不使用的对象占用堆（heap）空间足够大，使得应用程序无法满足下一次内存分配需求，就会导致OutOfMemoryError错误。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	The root cause of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.OutOfMemoryError: PermGen space</i>&nbsp;is exactly the same: the JVM needs to load the definition of a new class but there is not enough space in PermGen to do it &ndash; there are already too many classes stored there. A possible reason for this could be your application or server using too many classes for the current size of PermGen not to be able to accommodate them. Another commone reason could be a memory leak.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">java.lang.OutOfMemoryError: PermGen space错误的根本原因也是完全一样的：JVM需要加载一个新类的定义，而永久代（PermGen）的空间不足&mdash;&mdash;已经有太多的类存储在那里了。一个可能的原因是：你的应用程序或服务器使用了太多的类，当前的永久代（PermGen）大小无法满足需求。另一个原因可能是内存泄漏。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	Permanent Generation Leak</h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">永久代（内存）泄漏</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	But still, how on earth it is possible to leak something in PermGen? It holds definitions of java classes and they cannot become unused, can they? Actually, they can. In case of a Java web application deployed into an application server all those classes in your EAR/WAR become worthless when the application is undeployed. The JVM continues to run as the application server is still alive, but a whole bunch of class definitions are not in use anymore. And they should be removed from PermGen. If not, we will have memory leak in the PermGen area.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">但是，永久代（PermGen）到底有没有可能会内存泄漏？它保存了Java类定义，并且这些类定义是不会变成&ldquo;无用&rdquo;的，是吗？事实上，它们是可以变成&ldquo;无用&rdquo;的。以一个部署到应用程序服务器的Java web程序来说，当该应用程序被卸载的时候，你的EAR/WAR包中的所有类都将变得无用。只要应用程序服务器还活着，JVM将继续运行，但是一大堆的类定义将不再使用，理应将它们从永久代（PermGen）中移除。如果不移除的话，我们在永久代（PermGen）区域就会有内存泄漏。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	As a nice sample on the reasons &ndash; the Tomcat developers have set up a&nbsp;<a href="http://wiki.apache.org/tomcat/MemoryLeakProtection" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">Wiki page</a>&nbsp;describing different leaks found and fixed in the Apache Tomcat versions 6.0.24 and above.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">这里有一个说明内存泄漏的原因的好例子&mdash;&mdash;Tomcat的开发者们写了一个<a href="http://wiki.apache.org/tomcat/MemoryLeakProtection" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">Wiki页面</span></a>来描述在Apache Tomcat 6.0.24及之后的版本中发现、修复的不同的内存泄漏原因。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	Leaking Threads</h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">线程内存泄漏</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	One possible scenario for a classloader leak is through long running threads. This happens when your application or &ndash; as often was the case in my experience &ndash; a 3rd party library used by your application, starts some long running thread. An example of this could be a timer thread whose job is to execute some code periodically.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">类加载器（classloader）泄漏的一个可能的场景就是通过运行的线程（而内存泄漏）。当你的程序，或者你的程序使用的第三方库（我经常遇到这种情况）开启了一些长时间运行的线程。一个例子：一个用于周期性执行代码的计时器（timer）线程。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	If the intended lifespan of this thread is not fixed, we are heading directly into a trouble. When any part of your application ever starts a thread, you must make sure that it is not going to outlive the application. In typical cases the developer either is not aware of this responsibility or simply forgets to write the clean-up code.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">如果不解决该线程预期的生命周期问题，我们直接会遇到麻烦。当你程序的任何一部分启动一个线程的时候，你要确保它不会比程序活得还要久。在典型的情况下，开发者要么不知道自己有责任处理好这个问题，或者忘了写清理（clean-up）的代码。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Otherwise, if some thread continues to run after the application is undeployed, it will, usually, hold a reference to a classloader of the web application it was started by, called&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">context classloader</i>. Which in turn means that all classes of the undeployed application continue to be held in memory. Remedy? If it is your application that starts new threads, you should shut them down during undeployment using a servlet context listener. If it is a 3rd party library, you should search for its own specific shutdown hook. Or file a bug report if there is none.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">否则，如果应用程序卸载后，线程还在继续运行，它通常将维持一个到web应用程序的classloader的引用，即我们所说的context classloader。这也就意味着，所有卸载掉的应用程序仍然保存在内存中。怎么解决？如果是你的程序开启了新线程，那么你就应该在卸载的时候关闭它们，这可以通过使用一个servlet context listener来实现。如果是第三方库开启的新线程，你应该搜索它的关闭线程的接口，如果没有的话，就上报一个bug吧。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	<a style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213);">Leaking Drivers</a></h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">驱动内存泄漏</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Another typical case of a leak can be caused by database drivers. We have encountered this leak in&nbsp;<a href="http://plumbr.eu/download" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">our own demo application</a>&nbsp;that we ship with Plumbr. It is a slightly modified Pet Clinic application shipped along with Spring MVC. Let us highlight some things that happen when this application is being deployed to the server.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">另一个典型的内存泄漏原因是由数据库驱动造成的。我们在和Plumbr一起发布的<a href="http://plumbr.eu/download" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">demo程序</span></a>中遇到了这种内存泄漏情况。它是一个与Sprint MVC一起发布的、代码稍微修改过的Pet Clinic程序。让我们关注一下当这个应用程序部署到服务器上的时候，发生了什么：</span></span></span></p>
<ul style="box-sizing: border-box; margin: 10px 0px 20px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 12px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		The server creates a new instance of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.Classloader&nbsp;</i>and starts to load the application&rsquo;s classes using it.</li>
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">服务器创建一个java.lang.Classloader的新实例，并用它来加载程序的类。</span></span></span></li>
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		Since the PetClinic uses a HSQL database, it loads the corresponding JDBC driver,<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">org.hsqldb.jdbcDriver</i></li>
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">由于PetClinic使用了HSQL数据库，所以它会加载相应的JDBC驱动，即org.hsqldb.jdbcDriver</span></span></span></li>
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		This class, being a good-mannered JDBC driver, registers itself with&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.sql.DriverManager</i>&nbsp;during initialization, as required per JDBC specification. This registration includes storing inside a static field of DriverManager a reference to an instance of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">org.hsqldb.jdbcDriver</i>.</li>
<li style="box-sizing: border-box; margin-top: 4px; margin-left: 30px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Arial, Helvetica, sans-serif; color: black; list-style-image: url(http://plumbr.eu/wp-content/themes/plumbr/assets/images/bullet.jpg); list-style-type: none; margin-right: 0px !important; margin-bottom: 0px !important;">
		<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">这个JDBC驱动类会在初始化的时候将它注册到java.sql.DriverManager中（正如JDBC规范所要求的那样）。这个注册过程包括了存储org.hsqldb.jdbcDriver的一个实例的引用到DriverManager的一个静态域中</span></span></span></li>
</ul>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Now, when the application is undeployed from the application server, the&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.sql.DriverManager&nbsp;</i>will still hold that reference, as there is no code in the HSQLDB library nor in the Spring framework nor in the application to remove that! As was explained above, a&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">jdbcDriver</i>&nbsp;object still holds a reference to an<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">org.hsqldb.jdbcDriver&nbsp;</i>class, which in turn holds a reference to the instance of&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.Classloader</i>used to load the application. This classloader now still reference all the classes of the application. In case of our particular demo application, during application startup almost 2000 classes are loaded, occupying roughly 10MB in PermGen. Which means that it takes about 5-10 redeploys to fill the PermGen with default size to reach the&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">java.lang.OutOfMemoryError: PermGen space</i>&nbsp;crash.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">现在，当从服务器上卸载应用程序的时候，java.sql.DriverManager仍将持有那个引用，无论在HSQLDB库，或者在Spring framework中，都没有代码可以移除它！正如上面解释的那样，一个jdbcDriver对象将持有一个到org.hsqldb.jdbcDriver类的引用，从而持有用于加载应用程序的java.lang.Classloader的一个实例的引用。这个classloader现在仍然引用着应用程序的所有类。在我们那特殊的demo应用程序中，在程序启动的时候，需要加载将近2000个类，占用约10MB永久代（PermGen）内存。这就意味着需要5~10次重新部署，才会将默认大小的永久代（PermGen）塞满，然后就会触发java.lang.OutOfMemoryError: PermGen space错误并崩溃。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	How to fix that? One possibility is to write a servlet context listener, which de-registers the HSQLDB driver from&nbsp;<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">DriverManager</i>&nbsp;during application shutdown. This is pretty straightforward. But remember &ndash; you will have to write the corresponding code in every application using the driver.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">怎样解决此问题？一个可能的办法就是写一个servlet content listener，用于在应用程序关闭的时候，从DriverManager反注册HSQLDB驱动。这个方法很直接，但是请记住&mdash;&mdash;你需要在使用该驱动的每一个应用程序中都这么写。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	<a href="http://www.plumbr.eu/" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">Download</a>&nbsp;our latest version of Plumbr with our demo application and play with it to find out how the leak occurs, how Plumbr finds it and how do we explain the cause.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">下载我们最新版的Plumbr以及demo应用程序来研究一下，看看内存泄漏是怎么发生的，Plumbr是怎么发现它，以及我们是怎么解释其原因的。</span></span></span><br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a></p>
<h2 style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px 0px 15px; border: 0px; outline: 0px; font-size: 22px; vertical-align: baseline; font-family: Helvetica, Arial, sans-serif;">
	Conclusion</h2>
<p><span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">结论</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	There are many reasons why your application might encounter a<i style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;">&nbsp;java.lang.OutOfMemoryError: PermGen space</i>. The root cause for the majority of them is some reference to an object or a class loaded by the application&rsquo;s class loader that has died after that. Or a direct link to the class loader itself. The actions you need to take for remedy are quite similar for most of these causes. Firstly, find out where that reference is being held. Secondly, add a shutdown hook to your web application to remove the reference during application&rsquo;s undeployment. You can do that by either using a servlet context listener or by using the API provided by your 3rd party library.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">你的应用程序遇到java.lang.OutOfMemoryError: PermGen space错误的原因很多，究其根本原因，大多数是由于object或程序的class loader加载的类的引用已经无用了导致的。对此类问题，你需要采取的补救措施都非常相似，即，首先，找出引用在哪里被持有；其次，给你的web应用程序添加一个关闭的hook，或者在应用程序卸载后移除引用。你要么通过servlet context listener，要么通过第三方库提供的API来实现这一点。</span></span></span></p>
<p style="box-sizing: border-box; margin: 0px 0px 20px; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline; font-family: Helverica, Arial, sans-serif; color: rgb(60, 58, 55); line-height: 18px;">
	Finding those leaking references has never been easy. We ourselves have spent countless hours trying to trace down why some applications require 20MB of PermGen on each redeploy. But as of version 1.1,, Plumbr will show you the cause of the leak and give you a hint on how to fix it. If you want to try it out,&nbsp;<a href="http://www.plumbr.eu/" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">register and download the tool</a>. If you are running an older version of Plumbr, we strongly recommend downloading&nbsp;<a href="http://plumbr.eu/download" style="box-sizing: border-box; margin: 0px; padding: 0px; vertical-align: baseline; color: rgb(0, 156, 213); text-decoration: none;">an upgrade</a>.<br />
	<span style="color:#0000ff;"><span style="font-size: 14px;"><span style="font-family: 宋体;">找出内存泄漏的引用从来就不是件容易事。例如，就我们自己来说，我们曾经花费了数不清的时间去追踪为什么某些程序每一次部署都需要用掉20MB的永久代（PermGen）内存。但是，Plumbr 1.1版能告诉你内存泄漏的原因，并给出修复提示。如果你想试试，就去注册下载吧。如果你还在使用旧版的Plumbr，我们强烈建议你升级到新版。</span></span></span></p>
<p>	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
	转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
	感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
	<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>

			<!--[syntaxhighlighter]-->
			<!--代码高亮，请勿编辑-->
			<script type="text/javascript" src="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/scripts/shCore.js"></script><script type="text/javascript" src="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/scripts/shBrushJava.js"></script>

			<link type="text/css" rel="stylesheet" href="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/styles/shCoreCk.css" />
			<link type="text/css" rel="stylesheet" href="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/styles/shThemeCk.css" />
			<script type="text/javascript">
			SyntaxHighlighter.defaults['class-name']	= '';
			SyntaxHighlighter.defaults['smart-tabs']	= true;
			SyntaxHighlighter.defaults['tab-size']		= 2;
			SyntaxHighlighter.defaults['gutter']		= true;
			SyntaxHighlighter.defaults['quick-code']	= true;
			SyntaxHighlighter.defaults['collapse'] 		= false;
			SyntaxHighlighter.defaults['auto-links']	= true;
			SyntaxHighlighter.defaults['toolbar']		= true;
			SyntaxHighlighter.all();
			</script>
			<!--[/syntaxhighlighter]-->]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b%ef%bc%88%e7%bf%bb%e8%af%91%ef%bc%89%e4%bb%80%e4%b9%88%e6%98%afjava%e7%9a%84%e6%b0%b8%e4%b9%85%e4%bb%a3%ef%bc%88permgen%ef%bc%89%e5%86%85%e5%ad%98%e6%b3%84%e6%bc%8f/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[原创]摄像头娘来了</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b%e6%91%84%e5%83%8f%e5%a4%b4%e5%a8%98%e6%9d%a5%e4%ba%86/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b%e6%91%84%e5%83%8f%e5%a4%b4%e5%a8%98%e6%9d%a5%e4%ba%86/#comments</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Fri, 27 Jan 2012 18:07:31 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[综合]]></category>
		<category><![CDATA[motion detection]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[t.qq.com/cameragirl]]></category>
		<category><![CDATA[VC++发微博]]></category>
		<category><![CDATA[VC++运动检测]]></category>
		<category><![CDATA[代码]]></category>
		<category><![CDATA[内存泄漏]]></category>
		<category><![CDATA[微博]]></category>
		<category><![CDATA[微博API]]></category>
		<category><![CDATA[摄像头娘]]></category>
		<category><![CDATA[物联网]]></category>
		<category><![CDATA[萌娘]]></category>
		<category><![CDATA[运动检测]]></category>
		<category><![CDATA[饮水机娘]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=4341</guid>

					<description><![CDATA[<p>
被标题吸引进来的各位，抱歉，这还是一篇技术文章<img decoding="async" alt="crying" height="24" src="http://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/ckeditor/plugins/smiley/images/14.gif" title="crying" width="24" /><br />
本文的主角是一个摄像头及其配套程序，我毫无创意地模仿网上的流行说法，称之为&#8220;<span style="color:#ff0000;">摄像头娘</span>&#8221;。她会自己发微博，<span style="color:#0000ff;">记得follow她哦</span>：<strong><a href="http://t.qq.com/cameragirl" target="_blank" rel="noopener noreferrer"><span style="color:#ff0000;"><span style="background-color:#99ff99;">t.qq.com/cameragirl</span></span></a></strong></p>
<p>前段时间浙大有个博士做了个&#8220;<span style="color:#800080;">饮水机娘</span>&#8221;，也就是他实验室里的饮水机没水或者水加热开了的时候，都会自动发一条微博。这个与&#8220;物联网&#8221;扯上了关系的东西，让我原来以为那位博士是改造了饮水机，加上了温度及水位传感器，配合单片机，将饮水机的状态采集到PC中去，再写一个PC上的软件将触发的事件发送到微博上，后来看了相关的介绍，才知道没那么复杂，其实他没有改造饮水机，而是用一个摄像头对准饮水机的状态指示灯，根据指示灯的情况，来判断饮水机的状态。</p>
<p>于是我也就萌生了做一个&#8220;摄像头娘&#8221;的念头。<br />
我构想中的&#8220;摄像头娘&#8221;是这样的：当有物体在她面前运动的时候，她能感受得到，并且会为这个过客拍一张照片，然后发到微博上。<br />
下面是<span style="color:#ff0000;">实际效果截图</span>：<br />
<span id="more-4341"></span></p>
<div style="text-align: center; ">
	<a href="http://t.qq.com/cameragirl" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2012/01/weibo_snapshot.png" style="width: 542px; height: 330px; " /></a></div>
<p>好吧，说得傻一点就是：一个有运动检测（motion detection）功能的程序，当检测到摄像头前有物体在运动时，就将画面拍一张照片，发到微博上。<br />
确实，没啥创意，但是还是有一定实用性的。例如，你可以在家里放置一个&#8220;她&#8221;为你看家，假设你运气不好，有不速之客闯进了你家里，那么，只要他进入了&#8220;摄像头娘&#8221;的视角，就会马上被检测到，然后被拍上来，发到微博上，后面的事，哼哼，你就有第一手证据了。<br />
<span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
我是用<span style="color:#0000ff;">VC++ 2005</span>开发的这个软件，运行于Win7上，其实它很简单，功能主要分为两部分：运动检测（motion detection）&#38; 发微博，下面分别陈述。<br />
<span style="background-color:#afeeee;">【1】</span>运动检测 / motion detection<br />
从头自己写？那得多笨才能这样做啊！而且我也不是干视频处理这一行的，一时半会写不出来这玩意。<br />
久闻<a href="http://opencv.willowgarage.com/wiki/" target="_blank" rel="noopener noreferrer"><span style="background-color:#faebd7;">OpenCV</span></a>的大名，我知道它可以帮我实现想要的功能，所以毫不犹豫地选择了它。<br />
我以前也从未使用过OpenCV，从下载它的开发包开始，到参照网上的demo写出一个可用的motion detection程序，只花了半天多时间，由此可见OpenCV的强大。<br />
关于OpenCV的介绍，这里只摘取其官网的一句话：</p>
<blockquote>
<p>
		OpenCV (Open Source Computer Vision) is a library of programming functions for real time computer vision.</p></blockquote>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b%e6%91%84%e5%83%8f%e5%a4%b4%e5%a8%98%e6%9d%a5%e4%ba%86/" class="read-more">Read More </a>]]></description>
										<content:encoded><![CDATA[<p>
被标题吸引进来的各位，抱歉，这还是一篇技术文章<img decoding="async" alt="crying" height="24" src="http://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/ckeditor/plugins/smiley/images/14.gif" title="crying" width="24" /><br />
本文的主角是一个摄像头及其配套程序，我毫无创意地模仿网上的流行说法，称之为&ldquo;<span style="color:#ff0000;">摄像头娘</span>&rdquo;。她会自己发微博，<span style="color:#0000ff;">记得follow她哦</span>：<strong><a href="http://t.qq.com/cameragirl" target="_blank" rel="noopener noreferrer"><span style="color:#ff0000;"><span style="background-color:#99ff99;">t.qq.com/cameragirl</span></span></a></strong></p>
<p>前段时间浙大有个博士做了个&ldquo;<span style="color:#800080;">饮水机娘</span>&rdquo;，也就是他实验室里的饮水机没水或者水加热开了的时候，都会自动发一条微博。这个与&ldquo;物联网&rdquo;扯上了关系的东西，让我原来以为那位博士是改造了饮水机，加上了温度及水位传感器，配合单片机，将饮水机的状态采集到PC中去，再写一个PC上的软件将触发的事件发送到微博上，后来看了相关的介绍，才知道没那么复杂，其实他没有改造饮水机，而是用一个摄像头对准饮水机的状态指示灯，根据指示灯的情况，来判断饮水机的状态。</p>
<p>于是我也就萌生了做一个&ldquo;摄像头娘&rdquo;的念头。<br />
我构想中的&ldquo;摄像头娘&rdquo;是这样的：当有物体在她面前运动的时候，她能感受得到，并且会为这个过客拍一张照片，然后发到微博上。<br />
下面是<span style="color:#ff0000;">实际效果截图</span>：<br />
<span id="more-4341"></span></p>
<div style="text-align: center; ">
	<a href="http://t.qq.com/cameragirl" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2012/01/weibo_snapshot.png" style="width: 542px; height: 330px; " /></a></div>
<p>好吧，说得傻一点就是：一个有运动检测（motion detection）功能的程序，当检测到摄像头前有物体在运动时，就将画面拍一张照片，发到微博上。<br />
确实，没啥创意，但是还是有一定实用性的。例如，你可以在家里放置一个&ldquo;她&rdquo;为你看家，假设你运气不好，有不速之客闯进了你家里，那么，只要他进入了&ldquo;摄像头娘&rdquo;的视角，就会马上被检测到，然后被拍上来，发到微博上，后面的事，哼哼，你就有第一手证据了。<br />
<span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
我是用<span style="color:#0000ff;">VC++ 2005</span>开发的这个软件，运行于Win7上，其实它很简单，功能主要分为两部分：运动检测（motion detection）&amp; 发微博，下面分别陈述。<br />
<span style="background-color:#afeeee;">【1】</span>运动检测 / motion detection<br />
从头自己写？那得多笨才能这样做啊！而且我也不是干视频处理这一行的，一时半会写不出来这玩意。<br />
久闻<a href="http://opencv.willowgarage.com/wiki/" target="_blank" rel="noopener noreferrer"><span style="background-color:#faebd7;">OpenCV</span></a>的大名，我知道它可以帮我实现想要的功能，所以毫不犹豫地选择了它。<br />
我以前也从未使用过OpenCV，从下载它的开发包开始，到参照网上的demo写出一个可用的motion detection程序，只花了半天多时间，由此可见OpenCV的强大。<br />
关于OpenCV的介绍，这里只摘取其官网的一句话：</p>
<blockquote>
<p>
		OpenCV (Open Source Computer Vision) is a library of programming functions for real time computer vision.</p>
</blockquote>
<p>没错，大名鼎鼎的机器视觉开发库。<br />
我使用的版本是2.3.1，下载回来之后会看到，它里面已经没有VC++2005的lib了，只有2008及2010的（2005确实太老了），但是试验之后会发现，在VC++ 2005中用2008的lib完全没问题。<br />
<span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
运动检测的基本原理是：<span style="color:#0000cd;">获取摄像头的视频流&rarr;在很短的时间内连续抓取两帧图像&rarr;计算两幅图像的差值&rarr;计算直方图&rarr;判断某些指标是否超过了阈值&rarr;触发动作</span>。我没有图像处理的编码经验，所以也只能解释到这个业余的水平了。<br />
这种功能的代码在网上比比皆是，不过，很多都有陷阱：内存泄漏。基本上来说，都是错误使用cvCloneImage函数造成的。此函数如果放在一个循环中，会导致一次吃掉几M内存（对分辨率不高的图像来说），就算使用了cvReleaseImage函数来释放内存，似乎也没用&mdash;&mdash;应该是对OpenCV的熟悉度不足造成的，用法不对。网上一搜索，就会发现无数人都遇到了这样的问题，那些一篇又一篇的转载文章几乎都是说用同一种方法：以cvCopy来代替cvCloneImage实现同样的功能。<br />
我就是用这种方法解决了内存泄漏问题的，不过，在换成cvCopy之后还有许多其他的问题，一言难尽，看我下面的代码就知道正确的做法了。总之一句话：通过不断Google是可以搞定的这些问题的。<br />
下面，就来看看motion detection的核心代码吧（使用的OpenCV库文件为&nbsp;<span style="color:#008080;">opencv_core231.lib</span>，<span style="color:#008080;">opencv_highgui231.lib</span>，<span style="color:#008080;">opencv_imgproc231.lib</span>）：</p>
<pre class="brush:cpp;first-line:1;pad-line-numbers:true;highlight:null;collapse:false;">
/**
 * Author: Darran Zhang @ codelast.com
 * Date: 2012-01-27
 */

CvCapture* pCapture = cvCreateCameraCapture(0);  // 初始化摄像头 

if(NULL == pCapture) {
  MessageBox(_T(&quot;检测不到摄像头！&quot;), _T(&quot;错误&quot;), MB_ICONERROR);
  return;
}

string strCameraWindowName = &quot;摄像头&quot;;   // 窗口标题 
string strDiffWindowName = &quot;图像差值&quot;;   // 窗口标题 
cvNamedWindow(strCameraWindowName.c_str(), CV_WINDOW_AUTOSIZE); // 创建一个窗口，第二个参数使得用户不能手动改变窗口大小 
cvNamedWindow(strDiffWindowName.c_str(), CV_WINDOW_AUTOSIZE);

IplImage *pFrame = cvQueryFrame(pCapture);      // 随意获取一帧，这是为了通过这一帧取到其宽、高、像素位深、通道数，否则就无法正确创建其他帧图像 
IplImage *pFrameA = NULL;                       // 其中一帧 
IplImage *pFrameB = cvCreateImage(cvSize(pFrame-&gt;width, pFrame-&gt;height), pFrame-&gt;depth, pFrame-&gt;nChannels);     // 其中一帧 
IplImage *pFrameSub = cvCloneImage(pFrameB);    // A、B帧相减之后的帧 

int nDims = 256;     // 划分HIST的个数，越高越精确 
float hRangesArr[] = {0, 255};
float* hRanges = hRangesArr;

IplImage *pGrayscaleImage = NULL;  // 灰度图 
CvHistogram *pHist = cvCreateHist(1, &amp;nDims, CV_HIST_ARRAY, &amp;hRanges, 1);   // 创建直方图 
float fMaxValue = 0.0;

time_t ts = 0;   // 记录时间戳，用于防止在1秒内多次触发运动检测事件 

m_bActive = true;
while(m_bActive)
{
  pFrameA = cvQueryFrame(pCapture);  // 注意：cvQueryFrame返回的指针总是指向同一块内存 
  if(!pFrameA) {
    m_stcSD.strLatestErrMsg = _T(&quot;无法抓取视频帧&quot;);
    SendStatDataUpdateMsg();

    break;
  }

  cvAbsDiff(pFrameB, pFrameA, pFrameSub); // 计算两幅图像之差 
  cvCopy(pFrameA, pFrameB);               // 拷贝图像，第一个参数为源，第二个参数为目标 

  /* 显示摄像头图像 */
  cvMoveWindow(strCameraWindowName.c_str(), 150, 50);   // 设定窗口位置(x，y坐标) 
  cvShowImage(strCameraWindowName.c_str(), pFrameB);    // 显示图像，第2个参数指定了要显示的图像 

  /* 显示差值图像 */
  cvMoveWindow(strDiffWindowName.c_str(), 150, 400);
  cvShowImage(strDiffWindowName.c_str(), pFrameSub);

  /* 转换图像并计算直方图 */
  pGrayscaleImage = cvCreateImage(cvGetSize(pFrameSub), IPL_DEPTH_8U, 1);   // 创建灰度图 
  cvCvtColor(pFrameSub, pGrayscaleImage, CV_BGR2GRAY);                      // 将彩色图像转换为灰阶图像 
  cvCalcHist(&amp;pGrayscaleImage, pHist, 0, 0);                                // 计算直方图 

  /* 判断阈值是否超限，若超限则触发动作 */
  fMaxValue = 0.0;
  cvGetMinMaxHistValue(pHist, 0, &amp;fMaxValue, 0, 0);  // 找最大值，保存到fMaxValue中，第2个参数是最小值，不过我们不用 
  cvConvertScale(pHist-&gt;bins, pHist-&gt;bins, (fMaxValue ? (255.0 / fMaxValue) : 0.0), 0); // 缩放 bin 到区间 [0, 255] 

  double dRealtimeVal = cvGetReal1D(pHist-&gt;bins, 10);
  if (dRealtimeVal &gt; m_dDetectThreshold) {    // 判断是否大于预先设定的阈值 
    CTime ct = CTime::GetCurrentTime();
    time_t tsRef = ct.GetTime();
    if (tsRef - ts &gt;= 1) {
	  //TODO: 在此触发动作 
      ts = tsRef;
    }
  }

  cvReleaseImage(&amp;pGrayscaleImage);   // 释放内存 
  pGrayscaleImage = NULL;

  cvWaitKey(10);   // 等待若干毫秒 
}

cvReleaseCapture(&amp;pCapture);   // 停止捕获并释放摄像头资源 
cvReleaseHist(&amp;pHist);
cvReleaseImage(&amp;pFrameB);
cvReleaseImage(&amp;pFrameSub);

pCapture = NULL;
pHist = NULL;
pFrameB = NULL;
pFrameSub = NULL;
pFrame = NULL;
pFrameA = NULL;

cvDestroyWindow(strCameraWindowName.c_str());    // 销毁窗口 
cvDestroyWindow(strDiffWindowName.c_str());
</pre>
<p><span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
其中有几处要特别注意的代码：<br />
（1）<span style="color:#b22222;">IplImage *pFrameB = cvCreateImage(cvSize(pFrame-&gt;width, pFrame-&gt;height), pFrame-&gt;depth, pFrame-&gt;nChannels);</span><br />
创建B帧图像的时候，后面的参数都是通过pFrame来获取的，如果这几个参数你随意填写，那么就会导致程序编译可通过，运行时崩溃。所以我前面先抓取了一帧，再通过这一帧来获取B帧图像的参数，这样就完全符合了。<br />
（2）cvQueryFrame函数返回的指针总是指向同一块内存&mdash;&mdash;如果你不知道这一点，并且B帧图像是通过cvQueryFrame获取的，那么可能你调试了半天，却发现A帧、B帧的内容始终相同，二者相减得到的结果永远不变，motion detection就不起作用了。所以我把B帧图像用cvCreateImage来创建，A帧用cvQueryFrame来获取，这样就没有问题了。<br />
（3）由于两帧的检测时间较短，所以可能在多个瞬间触发多次动作，为此我根据时间戳做了一些简单的处理，来防止同一秒内触发多次动作。<br />
（4）其他的代码，各位看看就明白了，也无需过多的解释。</p>
<p><span style="background-color:#afeeee;">【2】</span>发微博<br />
我选择的是腾讯微博。<br />
不得不承认，腾讯微博对C++开发者的支持不足，下载回来的开发包里，仅仅同音的错别字就有一堆（可见开发API的同学用的是拼音输入法）。另外，还有各种各样的问题，例如，API Example是无法编译的&mdash;&mdash;原因是ApiType.cpp 和 ApiType.h没有被添加进工程中，导致找不到 CApiType 类定义。再看看它的代码中，编写风格非常随意，让人看了非常难受，总之就一句话，这API你就凑合着用吧！<br />
在动手写程序发微博之前，需要先在腾讯的网页上申请成为开发者，否则你的程序是无法用微博API来发微博的。申请的过程非常简单，当然你必须先有一个QQ号，登录后，在<a href="http://open.t.qq.com/develop.php" target="_blank" rel="noopener noreferrer"><span style="color:#ff0000;">这个页面</span></a>申请。申请之后，你需要在腾讯的网页上点击创建一个<strong><span style="color:#b22222;">客户端应用</span></strong>&mdash;&mdash;尽管这个时候，你还连程序都没有开始写，但是这是先决条件，不这样做的话，你就算写好了程序也用不了，所以我就把这个过程先全部做好了，再开始写程序的事。<br />
<span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
创建客户端应用的页面如下图所示：</p>
<div style="text-align: center; ">
	<a href="http://www.codelast.com" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="create a client app" src="http://www.codelast.com/wp-content/uploads/2012/01/create_client_app.png" style="width: 581px; height: 277px; " /></a></div>
<p>点击之后只需要填写少许内容就可以创建一个客户端应用了。<br />
创建应用之后，你可以看到你所创建的应用的<span style="color:#ff0000;">App Key</span>（一串数字）和<span style="color:#ff0000;">App Secret</span>（数字和字母的混合），这两个字符串在你写程序发微博的时候会用到：</p>
<div style="text-align: center; ">
	<a href="http://www.codelast.com" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2012/01/app_info.png" style="width: 350px; height: 280px; " /></a></div>
<p><span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
在获取了以上所需的信息后，用API发微博的过程大致可以描述为：<span style="color:#0000cd;">通过App Key和App Secret跳转到腾讯的页面去获取oauth_verifier&rarr;用户人工输入oauth_verifier&rarr;通过oauth_verifier获取Access Key和Access Secret&rarr;授权通过，可以发微博了</span>。<br />
这个过程的demo，在腾讯微博的API Example中很详细地作了演示，不过，它的编码风格非常不规范，乱就一个字，会耽误开发者的时间。<br />
正因为腾讯微博API Example已经有了例子，所以我在这里就不把发微博相关的代码全部放上来了，只是提醒大家注意以下几点：<br />
（1）你的VC++工程中，只需要引入腾讯微博API中的一个库文件：QWBlogAPI.lib。<br />
（2）你的VC++工程需要以下头文件：</p>
<pre class="brush:plain;first-line:1;pad-line-numbers:true;highlight:null;collapse:false;">
JsonParser.h
UtilString.h
WeiboApi.h
WeiboParam.h
XmlParser.h
weibo.h
</pre>
<p>（3）你的程序运行时，需要以下dll：</p>
<pre class="brush:plain;first-line:1;pad-line-numbers:true;highlight:null;collapse:false;">
curllib.dll
libeay32.dll
openldap.dll
QWBlogAPI.dll
ssleay32.dll
</pre>
<p>这些文件在腾讯微博开发包中都能找到。<br />
（4）发普通微博，option是用<span style="color:#0000ff;">TXWB_T_ADD</span>；发带图片的微博，option是用<span style="color:#0000ff;">TXWB_T_ADD_PIC</span>。图片作为参数添加到CWeiboParam对象中时，是需要特殊处理的（详见API Example）。<br />
（5）微博发不出去时，一定要检查发送内容中的&ldquo;content&rdquo;的字符串是什么编码的！别以为字符串里有内容就是对的，我在这上面折腾了不少时间，就是没注意这个问题。可以用API Example中的<span style="color:#a52a2a;">Unicode2Mbcs</span>函数来转换一个CString到合适的字符串，很管用。<br />
（6）oauth_verifier的值，是在腾讯网页的地址中获取到的，而不是在网页正文中。你跳转到类似于如下的网址后（其实就是腾讯首页）：</p>
<pre class="brush:plain;first-line:1;pad-line-numbers:true;highlight:null;collapse:false;">
http://www.qq.com/?oauth_token=XXX&oauth_verifier=XXX&openid=&openkey=
</pre>
<p>就可以肉眼识别出其值了。<br />
<span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
<span style="background-color: rgb(175, 238, 238); ">【3】</span>软件UI<br />
UI很简单，但是调起来烦人。就截几幅图吧：</p>
<div style="text-align: center; ">
	<a href="http://www.codelast.com" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2012/01/cameragirl_ui_1.png" style="width: 438px; height: 324px; " /></a><br />
	&nbsp;</div>
<div style="text-align: center; ">
	<a href="http://www.codelast.com" target="_blank" rel="noopener noreferrer"><img decoding="async" alt="" src="http://www.codelast.com/wp-content/uploads/2012/01/cameragirl_ui_2.png" style="width: 405px; height: 313px; " /></a></div>
<p><span style="color: rgb(255, 255, 255); ">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255); ">http://www.codelast.com/</span></a><br />
好了，先暂时到这吧，在折腾的道路上...</p>
<p><span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
	<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>

			<!--[syntaxhighlighter]-->
			<!--代码高亮，请勿编辑-->
			<script type="text/javascript" src="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/scripts/shCore.js"></script><script type="text/javascript" src="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/scripts/shBrushCpp.js"></script>
<script type="text/javascript" src="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/scripts/shBrushPlain.js"></script>

			<link type="text/css" rel="stylesheet" href="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/styles/shCoreCk.css" />
			<link type="text/css" rel="stylesheet" href="https://www.codelast.com/wp-content/plugins/ck-and-syntaxhighlighter/syntaxhighlighter/styles/shThemeCk.css" />
			<script type="text/javascript">
			SyntaxHighlighter.defaults['class-name']	= '';
			SyntaxHighlighter.defaults['smart-tabs']	= true;
			SyntaxHighlighter.defaults['tab-size']		= 2;
			SyntaxHighlighter.defaults['gutter']		= true;
			SyntaxHighlighter.defaults['quick-code']	= true;
			SyntaxHighlighter.defaults['collapse'] 		= false;
			SyntaxHighlighter.defaults['auto-links']	= true;
			SyntaxHighlighter.defaults['toolbar']		= true;
			SyntaxHighlighter.all();
			</script>
			<!--[/syntaxhighlighter]-->]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b%e6%91%84%e5%83%8f%e5%a4%b4%e5%a8%98%e6%9d%a5%e4%ba%86/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
	</channel>
</rss>
