网站是用什么做的吗做月季评分表的工程网站叫什么
news/
2025/10/6 16:05:02/
文章来源:
网站是用什么做的吗,做月季评分表的工程网站叫什么,商丘购物网站开发设计,wordpress链接形式前言Java通过垃圾回收机制#xff0c;可以自动的管理内存#xff0c;这对开发人员来说是多么美好的事啊。但垃圾回收器并不是万能的#xff0c;它能够处理大部分场景下的内存清理、内存泄露以及内存优化。但它也并不是万能的。不然#xff0c;我们在实践的过程中也不会出现… 前言Java通过垃圾回收机制可以自动的管理内存这对开发人员来说是多么美好的事啊。但垃圾回收器并不是万能的它能够处理大部分场景下的内存清理、内存泄露以及内存优化。但它也并不是万能的。不然我们在实践的过程中也不会出现那么多因内存泄露导致的生产事件了。但很多内存泄露时间也是因为开发人员使用不当导致的。本篇文章我们就来聊聊内存泄露的原因是什么如何识别内存泄露以及如果在应用程序中进行处理。什么是内存泄露什么是内存泄露通俗的来说就是堆中的一些对象已经不会再被使用了但垃圾收集器却无法将它们从内存中清除。内存泄漏很严重的问题因为它会阻塞内存资源并随着时间的推移降低系统性能。如果不进行有效的处理最终的结果将会使应用程序耗尽内存资源无法正常服务导致程序崩溃抛出java.lang.OutOfMemoryError异常。堆内存中通常有两种类型的对象被引用的对象和未被引用的对象。被引用的对象是应用程序中仍然具有活跃的引用而未被引用的对象则没有任何活跃的引用。垃圾收集器会回收那些未被引用的对象但不会回收那些还在被引用的对象。这也是内存泄露发生的源头。内存泄露往往有以下表象当应用程序长时间连续运行时性能严重下降抛出OutOfMemoryError异常程序莫名其妙的自动崩溃应用程序耗尽链接对象当然如果打印GC日志有些场景下还会看到频繁执行full GC等状况。下面就具体分析一下这些场景和处理方案。Java中内存泄露分类在任何一个应用程序中发生内存泄露往往由很多原因构成。下面我们就聊聊最常见的一些内存泄露场景。1.静态属性导致内存泄露会导致内存泄露的一种情况就是大量使用static静态变量。在Java中静态属性的生命周期通常伴随着应用整个生命周期除非ClassLoader符合垃圾回收的条件。下面来看一个具体的会导致内存泄露的实例public class StaticTest {public static ListDouble list new ArrayList();public void populateList() {for (int i 0; i 10000000; i) {list.add(Math.random());}Log.info(Debug Point 2);}public static void main(String[] args) {Log.info(Debug Point 1);new StaticTest().populateList();Log.info(Debug Point 3);}
}
如果监控内存堆内存的变化会发现在打印Point1和Point2之间堆内存会有一个明显的增长趋势图。但当执行完populateList方法之后对堆内存并没有被垃圾回收器进行回收。上图为VisualVM监控显示的信息关于VisualVM的使用这里就不再赘述了可参考文章《没有监控过JVM内存的职场生涯是不完美的》。但针对上述程序如果将定义list的变量前的static关键字去掉再次执行程序会发现内存发生了具体的变化。VisualVM监控信息如下图对比两个图可以看出程序执行的前半部分内存使用情况都一样但当执行完populateList方法之后后者不再有引用指向对应的数据垃圾回收器便进行了回收操作。因此我们要十分留意static的变量如果集合或大量的对象定义为static的它们会停留在整个应用程序的生命周期当中。而它们所占用的内存空间本可以用于其他地方。那么如何优化呢第一进来减少静态变量第二如果使用单例尽量采用懒加载。2.未关闭的资源无论什么时候当我们创建一个连接或打开一个流JVM都会分配内存给这些资源。比如数据库链接、输入流和session对象。忘记关闭这些资源会阻塞内存从而导致GC无法进行清理。特别是当程序发生异常时没有在finally中进行资源关闭的情况。这些未正常关闭的连接如果不进行处理轻则影响程序性能重则导致OutOfMemoryError异常发生。如果进行处理呢第一始终记得在finally中进行资源的关闭第二关闭连接的自身代码不能发生异常第三Java7以上版本可使用try-with-resources代码方式进行资源关闭。3.不当的equals方法和hashCode方法实现当我们定义个新的类时往往需要重写equals方法和hashCode方法。在HashSet和HashMap中的很多操作都用到了这两个方法。如果重写不得当会造成内存泄露的问题。下面来看一个具体的实例public class Person {public String name;public Person(String name) {this.name name;}
}
现在将重复的Person对象插入到Map当中。我们知道Map的key是不能重复的。Test
public void givenMap_whenEqualsAndHashCodeNotOverridden_thenMemoryLeak() {MapPerson, Integer map new HashMap();for(int i0; i100; i) {map.put(new Person(jon), 1);}Assert.assertFalse(map.size() 1);
}
上述代码中将Person对象作为key存入Map当中。理论上当重复的key存入Map时会进行对象的覆盖不会导致内存的增长。但由于上述代码的Person类并没有重写equals方法因此在执行put操作时Map会认为每次创建的对象都是新的对象从而导致内存不断的增长。VisualVM中显示信息如下图 当重写equals方法和hashCode方法之后Map当中便只会存储一个对象了。方法的实现如下public class Person {public String name;public Person(String name) {this.name name;}Overridepublic boolean equals(Object o) {if (o this) return true;if (!(o instanceof Person)) {return false;}Person person (Person) o;return person.name.equals(name);}Overridepublic int hashCode() {int result 17;result 31 * result name.hashCode();return result;}
}
经过上述修改之后Assert中判断Map的size便会返回true。Test
public void givenMap_whenEqualsAndHashCodeNotOverridden_thenMemoryLeak() {MapPerson, Integer map new HashMap();for(int i0; i2; i) {map.put(new Person(jon), 1);}Assert.assertTrue(map.size() 1);
}
重写equals方法和hashCode方法之后堆内存的变化如下图 另外的例子就是当使用ORM框架如Hibernate时会使用equals方法和hashCode方法进行对象的的分析和缓存操作。如果不重写这些方法则发生内存泄漏的可能性非常高因为Hibernate将无法比较对象每次都是新对象然后不停的更新缓存。如何进行处理第一如果创建一个实体类总是重写equals方法和hashCode方法第二不仅要覆盖默认的方法实现而且还要考虑最优的实现方式4.外部类引用内部类这种情况发生在非静态内部类匿名类中在类初始化时内部类总是需要外部类的一个实例。每个非静态内部类默认都持有外部类的隐式引用。如果在应用程序中使用该内部类的对象即使外部类使用完毕也不会对其进行垃圾回收。假设一个类其中包含大量笨重对象的引用并且具有一个非静态内部类。当我们创建内部类的对象时内存模型如下所示如果将内部类声明为static的那么内存曲线则像从写equals和hashCode方法之后的图一样是一条平稳的直线。此种情况之所以发生内存泄露是因为内部类对象隐含的持有外部类的引用从而导致外部类成为垃圾对象时却无法被正常回收。使用匿名类的时候也会发生类似的情况。如何避免此种情况如果内部类不需要访问外部类的成员信息可以考虑将其转换为静态内部类。5.finalize()方法使用finalize()方法会存在潜在的内存泄露问题每当一个类的finalize()方法被重写时该类的对象就不会被GC立即回收。GC会将它们放入队列进行最终确定在以后的某个时间点进行回收。如果finalize()方法重写的不合理或finalizer队列无法跟上Java垃圾回收器的速度那么迟早应用程序会出现OutOfMemoryError异常。假设某个类重写了finalize()方法并且重写的方法在执行时需要一些时间。如果存在大量该对象垃圾回收时在VisualVM中的曲线如下 如果去掉重写的finalize()方法同样的程序展示的曲线如下 如果避免此种情况发生呢始终避免使用finalizer。6.String的intern方法字符串常量池在Java7中从PermGen移动到了堆空间。在Java6及以前版本我们使用字符串时要多加小心。如果读取了一个大字符串对象并且调用其intern方法intern()会将String放在JVM的内存池中PermGen而JVM的内存池是不会被GC的。同样会造成程序性能降低和内存溢出问题。JDK1.6中PermGen中存储大对象示例如何避免此种情况发生第一最简单的方式是更新JDK版到7及以上第二如果无法避免则可调整PermGen大小避免OutOfMemoryErrors溢出。PermGen相关配置-XX:MaxPermSize512m
7.使用ThreadLocalThreadLocal提供了线程本地变量它可以保证访问到的变量属于当前线程每个线程都保存有一个变量副本每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离将变量与线程相绑定从而实现线程安全的特性。ThreadLocal的实现中每个Thread维护一个ThreadLocalMap映射表key是ThreadLocal实例本身value是真正需要存储的Object。ThreadLocalMap使用ThreadLocal的弱引用作为key如果一个ThreadLocal没有外部强引用来引用它那么系统GC时这个ThreadLocal势必会被回收这样一来ThreadLocalMap中就会出现key为null的Entry就没有办法访问这些key为null的Entry的value。如果当前线程迟迟不结束的话这些key为null的Entry的value就会一直存在一条强引用链Thread Ref - Thread - ThreaLocalMap - Entry - value永远无法回收造成内存泄漏。如何解决此问题第一使用ThreadLocal提供的remove方法可对当前线程中的value值进行移除第二不要使用ThreadLocal.set(null) 的方式清除value它实际上并没有清除值而是查找与当前线程关联的Map并将键值对分别设置为当前线程和null。第三最好将ThreadLocal视为需要在finally块中关闭的资源以确保即使在发生异常的情况下也始终关闭该资源。try {threadLocal.set(System.nanoTime());//... further processing
} finally {threadLocal.remove();
}
处理内存泄漏的其他策略尽管在处理内存泄漏时没有万能的解决方案但是有一些方法可以使内存泄漏最小化。启用分析我们可通过一些工具用来对应用应用程序的内存使用情况等进行监控和诊断从而找到最佳的利用系统资源的方案。类似的工具有前面我们提到的VisualVM还有Mission ControlJProfilerYourKitJava VisualVM和Netbeans Profiler等。显示垃圾回收详情通过启用垃圾收集详情日志可以对GC的详细进行跟踪。通过以下命令进行启动-verbose:gc
通过添加此参数我们可以看到GC内部发生的情况的详细信息使用引用对象避免内存泄漏在Java中我们还可以使用java.lang.ref包内置引用对象来处理内存泄漏。使用java.lang.ref包而不是直接引用对象我们对对象使用特殊的引用从而确保它们可以轻松地被垃圾回收。IDE警告无论是Eclipse还是IDEA如果安装对应的插件比如阿里巴巴开发手册插件等当写代码中出现内存泄露风险代码时IDE会进行警告提醒从而从源头上避免内存泄露的代码出现在生产环境。基准测试通过执行基准测试来衡量和分析Java代码的性能从而选择更合理的解决方案。Code Review这也是最古老最有效的方式之一通过经验丰富的开发人员对代码的Review或多人进行Review从而达到查漏补缺的效果排除一些常见的内存泄露问题。小结本文介绍了内存泄露的原因以及常见的7种内存泄露场景针对每种内存泄露的场景都提供了解决方案。另外还为大家提供了6种额外的通用性解决策略。但针对内存泄露来说这还是九牛一毛不同的代码不同的场景都会出现一些未知的内存泄露问题同时也没有万能的解决方案。这就需要我们了解内存泄露的根本原因同时掌握一些基本的分析方法和策略以便灵活应对。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/929482.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!