2009-11-30 10 views
7

Tôi đang chạy Ứng dụng web Java trong Tomcat. Ứng dụng sử dụng khung công tác Quartz để lên lịch công việc định kỳ. Công việc cron này liên quan đến việc phân tích cú pháp tệp xml 4+ MB, mà tôi đang làm bằng cách sử dụng API JDOM. Tệp xml chứa khoảng 3600 nút được phân tích cú pháp và do đó dữ liệu được cập nhật trong DB mà tôi đang thực hiện tuần tự.
Sau khi phân tích cú pháp gần một nửa tệp, ứng dụng của tôi sẽ ném ngoại lệ ra khỏi bộ nhớ. Dấu vết ngăn xếp giống nhau là:Java hết bộ nhớ Ngoại lệ

Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]" java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOfRange(Arrays.java:3210) 
     at java.lang.String.<init>(String.java:216) 
     at java.lang.StringBuffer.toString(StringBuffer.java:585) 
     at org.netbeans.lib.profiler.server.ProfilerRuntimeMemory.traceVMObjectAlloc(ProfilerRuntimeMemory.java:170) 
     at java.lang.Throwable.getStackTraceElement(Native Method) 
     at java.lang.Throwable.getOurStackTrace(Throwable.java:590) 
     at java.lang.Throwable.getStackTrace(Throwable.java:582) 
     at org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:155) 
     at org.apache.juli.logging.DirectJDKLog.error(DirectJDKLog.java:135) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1603) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590) 
     at java.lang.Thread.run(Thread.java:619) 
Exception in thread "*** JFluid Monitor thread ***" java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOf(Arrays.java:2760) 
     at java.util.Arrays.copyOf(Arrays.java:2734) 
     at java.util.Vector.ensureCapacityHelper(Vector.java:226) 
     at java.util.Vector.add(Vector.java:728) 
     at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.updateSurvGenData(Monitors.java:230) 
     at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.run(Monitors.java:169) 
Nov 30, 2009 2:22:05 PM org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor processChildren 
SEVERE: Exception invoking periodic operation: 
java.lang.OutOfMemoryError: Java heap space 
     at java.lang.StringCoding$StringEncoder.encode(StringCoding.java:232) 
     at java.lang.StringCoding.encode(StringCoding.java:272) 
     at java.lang.String.getBytes(String.java:946) 
     at java.io.UnixFileSystem.getLastModifiedTime(Native Method) 
     at java.io.File.lastModified(File.java:826) 
     at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1175) 
     at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1269) 
     at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:296) 
     at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:118) 
     at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1337) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1601) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610) 
     at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590) 
     at java.lang.Thread.run(Thread.java:619) 
ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception: 
java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOfRange(Arrays.java:3210) 
     at java.lang.String.<init>(String.java:216) 
     at java.lang.StringBuffer.toString(StringBuffer.java:585) 
     at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296) 
     at java.util.HashMap.get(HashMap.java:300) 
     at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085) 
     at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882) 
     at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80) 
     at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168) 
     at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173) 
     at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159) 
     at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38) 
     at org.quartz.core.JobRunShell.run(JobRunShell.java:207) 
     at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525) 
DEBUG [ExceptionHelper]: Detected JDK support for nested exceptions. 
ERROR [ErrorLogger]: Job (updateVendorData.quoteUpdate threw an exception. 
org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.OutOfMemoryError: Java heap space] 
     at org.quartz.core.JobRunShell.run(JobRunShell.java:216) 
     at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525) 
Caused by: java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOfRange(Arrays.java:3210) 
     at java.lang.String.<init>(String.java:216) 
     at java.lang.StringBuffer.toString(StringBuffer.java:585) 
     at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296) 
     at java.util.HashMap.get(HashMap.java:300) 
     at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085) 
     at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882) 
     at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80) 
     at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168) 
     at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173) 
     at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159) 
     at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38) 
     at org.quartz.core.JobRunShell.run(JobRunShell.java:207) 

Điều này làm cho cả tomcat của tôi bị lỗi. Bạn có thể giúp tôi trong việc chẩn đoán vấn đề. Tôi thậm chí đã kích hoạt profiling trong Netbeans cho cùng nhưng có vẻ như ngay cả khi bị rơi. Tôi đã giữ bộ nhớ mặc định được cấp cho Tomcat. Có rò rỉ bộ nhớ nào không. DB của tôi là postgres và JDK là 1.6.0_15.

Cảm ơn, Amit

+4

đừng quên chọn câu trả lời cho câu hỏi này và câu hỏi trước; bạn đã thực hiện 7 câu hỏi và không ai trong số họ có câu trả lời hay? –

Trả lời

5

Mỗi lần bạn sử dụng một DOM để phân tích một tập tin XML, bạn sẽ tải toàn bộ tập tin vào bộ nhớ và cơ sở hạ tầng DOM sẽ sử dụng khoảng cùng kích thước để xử lý nó, vì vậy nó sẽ tiêu thụ khoảng hai lần bộ nhớ hơn kích thước tệp của bạn.

Bạn sẽ cần sử dụng SAX, trình phân tích cú pháp dựa trên sự kiện. Mặc dù điều này có thể khó hiểu trong lần đầu tiên nhưng nó rất hiệu quả, vì nó chỉ giữ trong nút phân tích cú pháp hiện tại của bộ nhớ.

Dường như Java có một số triển khai SAX, như StAX, tôi hy vọng điều đó sẽ hữu ích.

+0

Xin chào Rubens, tôi đang sử dụng JDOM để phân tích cú pháp XML lớn và nó sử dụng trình phân tích cú pháp SAX trong nội bộ. Mã phân tích cú pháp của tôi là: SAXBuilder builder = new SAXBuilder(); Document doc = builder.build (inputResource); Element elem = doc.getRootElement(); – Amit

+1

vì trình phân tích cú pháp DOM của bạn sử dụng SAX, bạn nên đọc XML theo tuần tự và tránh sử dụng '..',' // 'và các công cụ –

+0

StAX và SAX là các API khác nhau. Tuy nhiên, cả hai đều có thể được sử dụng để giảm mức sử dụng bộ nhớ. (SAX sử dụng gọi lại, trong mã người dùng StAX yêu cầu mã thông báo được phân tích tiếp theo) –

0

Bạn có chắc chắn không có bản sao mảng đệ quy nào đó, bị bỏ sót ở đó do nhầm lẫn không? Có lẽ trong các chủ đề khác nhau?

+0

Xin chào Lorenzog, tôi không sử dụng bất kỳ chuỗi nào, vì mục đích này và không có bản sao mảng. Tôi đang sử dụng JDOM để phân tích cú pháp các tập tin XML và tôi đoán nó sử dụng ArrayList để thực hiện nó. Đó có phải là vấn đề không? Có khả năng rò rỉ bộ nhớ nào không? – Amit

0

Tôi sẽ là điểm thứ hai về tệp và DOM chiếm nhiều bộ nhớ. Tôi cũng tự hỏi khi nào tôi thấy điều này:

ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception: 
    java.lang.OutOfMemoryError: Java heap space 
    at java.util.Arrays.copyOfRange(Arrays.java:3210) 

Sao chép đang làm gì? Tôi tự hỏi nếu có điều gì khác xấu xảy ra trong mã của bạn.

Nếu bạn đã nhận được điều này đến nay, nó cho thấy rằng bạn đã đọc tệp và DOM thành công và bạn bắt đầu ghi vào cơ sở dữ liệu. Bộ nhớ tệp đã được khôi phục.

Tôi khuyên bạn nên xem bộ nhớ bằng cách sử dụng VisualGC để bạn có thể xem điều gì đang xảy ra.

+0

Sao chép đó là nội bộ của StringBuffer. – BalusC

+0

Hi duffy, cảm ơn bạn đã trả lời. JDOM có thể thực hiện việc sao chép nội bộ này không? Luôn luôn tôi đã kích hoạt hồ sơ cho các ứng dụng và có 2 lớp tồn tại ngay cả sau khi GC đã chạy, họ là org.postgresql.jdbc4.Jdbc4PrepareStatement và org.postgresql.jdbc4.Jdbc4ResultSet. Đây có thể là nguyên nhân gây rò rỉ bộ nhớ trong ứng dụng không? – Amit

+0

Có thể. Không thể nói không có mã, nhưng nếu bạn không đóng những mã này đúng cách, bạn có thể bị rò rỉ tài nguyên sẽ mang lại cho bạn đau buồn. – duffymo

2

Phân tích cú pháp XML là một nhiệm vụ khá tốn kém. Trình phân tích cú pháp DOM trung bình sẽ cần ít nhất năm lần không gian bộ nhớ như tài liệu XML lớn. Bạn cũng nên tính đến điều này. Để đảm bảo rằng không có rò rỉ bộ nhớ ở một nơi nào khác gây ra tình trạng thiếu bộ nhớ cho trình phân tích cú pháp XML, bạn thực sự cần chạy một trình lược tả. Cung cấp cho nó nhiều bộ nhớ hơn, tăng gấp đôi bộ nhớ có sẵn và cấu hình nó. Khi bạn đã đóng đinh nguyên nhân xuống và cố định rò rỉ, thì bạn có thể quay trở lại bộ nhớ "mặc định" và kiểm tra lại. Hoặc nếu thực sự không có bất kỳ phương tiện nào bị rò rỉ, thì chỉ cần cung cấp cho nó bộ nhớ nhiều hơn một chút so với mặc định để tất cả đều phù hợp.

Bạn cũng có thể xem xét sử dụng trình phân tích cú pháp XML hiệu quả hơn cho bộ nhớ thay thế, ví dụ VTD-XML (homepage here, benchmarks here).

1

Bạn đã thử đặt kích thước heap tối đa lớn hơn để xem sự cố vẫn xảy ra sau đó? Có thể thậm chí không bị rò rỉ chút nào. Nó có thể chỉ là kích thước mặc định heap (64m trên Windows tôi nghĩ) là không đủ cho quá trình này đặc biệt.

Tôi thấy rằng tôi hầu như luôn luôn cần phải cung cấp cho bất kỳ ứng dụng tôi đang chạy Tomcat nhiều heap và không gian gen perm hơn mặc định hoặc tôi sẽ chạy ra khỏi vấn đề bộ nhớ. Nếu bạn cần trợ giúp điều chỉnh cài đặt bộ nhớ, hãy xem this question.

+0

Xin chào Jason, cảm ơn bạn đã trả lời. Tôi tăng kích thước heap của mình và ứng dụng hoạt động tốt. Tôi đã thiết lập hồ sơ cho ứng dụng của tôi và sau khi quá trình được hoàn thành, có thể tìm thấy 2 loại đối tượng được cấp phát trực tiếp không được deallocated bởi bộ thu gom rác, chúng là org.postgresql.jdbc4.Jdbc4PrepareStatement và org.postgresql.jdbc4.Jdbc4ResultSet. Đây có thể là nguyên nhân gây rò rỉ bộ nhớ trong ứng dụng không? – Amit

+0

Đó có thể là dấu hiệu cho thấy bạn không đóng đúng đối tượng JDBC. Bạn đang tự thực hiện các cuộc gọi JDBC hay bạn đang sử dụng một khung công tác (như Spring) để bao bọc các cuộc gọi JDBC của bạn? Nếu bạn gọi trực tiếp JDBC, hãy đảm bảo bạn gọi phương thức close() trên bất kỳ đối tượng ResultSet, Statement, PreparedStatement và Connection nào trong khối cuối cùng khi bạn sử dụng xong chúng. –

+0

Với khả năng tốt nhất của tôi, tôi đã quan tâm rằng tôi đóng tất cả các kết nối mà tôi đã mở nhưng vấn đề vẫn tồn tại. Có cách nào bạn có thể giúp tôi với điều này – Amit

0

Bạn có thể chạy ứng dụng của mình bằng: -XX: + HeapDumpOnOutOfMemoryError. Điều này sẽ làm cho JVM tạo ra một bãi chứa đống khi nó hết bộ nhớ. Bạn có thể sử dụng một cái gì đó như: MAT hoặc JHAT để xem những gì các đối tượng đang được tổ chức vào. Tôi khuyên bạn nên sử dụng công cụ phân tích bộ nhớ nhật thực (MAT) trên vùng kết xuất được tạo ra vì nó khá đơn giản để sử dụng: http://www.eclipse.org/mat/

Tất nhiên bạn sẽ cần phải có một số ý tưởng về những vật thể có thể treo xung quanh để hữu ích. Đối tượng DOM? Tài nguyên từ các tài liệu xml trước đây? Kết nối cơ sở dữ liệu? MAT sẽ cho phép bạn theo dõi các tài liệu tham khảo trở lại một đối tượng gốc từ một số đối tượng mà bạn nghi ngờ nên có được thu gom rác thải.

9

Thử tăng phân bổ ram cho JVM của bạn. Nó sẽ giúp.

Fix for eclipse: Bạn có thể cấu hình này trong sở thích nhật thực như sau

  1. Windows -> Preferences (trên mac của nó: eclipse -> tuỳ chọn)
  2. Java -> JRE cài đặt
  3. Chọn JRE và nhấp vào Chỉnh sửa
  4. trên loại trường đối số VM mặc định trong -Xmx1024M. (hoặc sở thích bộ nhớ của bạn, cho 1 gb ram của nó 1024)
  5. Nhấp vào kết thúc hoặc OK.
+1

Cảm ơn bạn nó hoạt động –

2

Bạn phải cấp thêm khoảng trống cho PermGenSpace của JVM tomcat.

này có thể được thực hiện với sự tranh luận JVM: -XX:MaxPermSize=128m

Theo mặc định, không gian PermGen là 64M (và nó chứa tất cả các lớp học biên soạn, vì vậy nếu bạn có rất nhiều jar (lớp) trong classpath của bạn, bạn thực sự có thể lấp đầy không gian này).

Trên một mặt lưu ý, bạn có thể theo dõi kích thước của không gian với JVisualVM PermGen và thậm chí bạn có thể kiểm tra nội dung của nó với YourKit Java Profiler

3

Hãy thử tăng phân bổ ram cho JVM của bạn. Nó sẽ giúp.

Fix for eclipse: Bạn có thể cấu hình này trong sở thích nhật thực như sau

Windows -> Preferences (trên mac của nó: eclipse -> tuỳ chọn) Java -> JRE cài đặt Chọn JRE và click vào Edit trên mặc định Loại trường đối số VM trong - Xms256m -Xmx512m -XX: MaxPermSize = 512m -XX: PermSize = 128m. (hoặc sở thích bộ nhớ của bạn, cho 1 gb ram của nó 1024) Nhấp vào kết thúc hoặc OK.