달력

42025  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

S-DES 소스 - java

2009. 10. 1. 11:33

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

POI - 시트복사

java 2008. 11. 20. 15:18

1. POI 의 API 이용
 - 2.5  에서 셀병합된 시트 복사에 대한 버그패치가 있었다.
sheet = wb.cloneSheet(0);

2. coding

private void copySheet(HSSFSheet from, HSSFSheet to, int fromRowCnt, int toRowCnt) {
    HSSFRow fromRow = null;
    HSSFRow toRow = null;
    for(int i = fromRowCnt ; i <= toRowCnt ; i++){
        fromRow = from.getRow(i);
        toRow = to.createRow(i);
        Iterator<HSSFCell> iterator = fromRow.cellIterator();
        short col = 0;
        while(iterator.hasNext())
            addCell(toRow, col++, iterator.next().getStringCellValue());
    }
}

'java' 카테고리의 다른 글

S-DES 소스 - java  (0) 2009.10.01
자바 디자인 패턴 1 - Iterator  (0) 2008.09.24
The Java XML Validation API - IBM developerworks  (0) 2008.09.19
Heap Dump 생성  (0) 2008.09.18
VM 분석 : Chapter 1 Performance - 2 / -Xrunprof 옵션  (0) 2008.09.17
Posted by marryjane
|

http://iilii.egloos.com/3788564

1. iterator 패턴은..

프로그래밍을 하다 보면, array나 List, Set, Map과 같은 애들을 많이 씁니다. 얘네들의 특징은 어떤 데이터들의 집합체라는 겁니다. 원래 집합체란 게 속에 뭐가 들었냐가 중요하죠. 그래서 집합체들을 다룰 때는 얘들이 가지고 있는 개별 원소에 대해서 이런 저런 작업들을 할 일이 많습니다.
iterator를 쓰게 되면, 집합체와 개별 원소들간에 분리시켜 생각할 수가 있습니다. 심지어는 그 집합체가 어떤 클래스의 인스턴스인지 조차 신경쓰지 않아도 됩니다.

2. 예제

package c01_iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MagicianList implements Iterable<String> {
        private List<String> list = new ArrayList<String>();

        public void add(String name){
                list.add(name);
        }

        public Iterator<String> iterator() {
                return  new Iterator<String>(){
                        int seq = 0;
                        public boolean hasNext() {
                                return  seq < list.size();
                        }
                        public String next() {
                                return list.get(seq++);
                        }
                        public void remove() {
                        }
                };
        }

        public static void main(String[] arg){
                MagicianList magicians = new MagicianList();
                magicians.add("이은결");
                magicians.add("Kevin parker");
                magicians.add("David Blaine");

                Iterator<String> iterator = magicians.iterator();
                while (iterator.hasNext()) {
                        String element = iterator.next();
                        System.out.println(element);
                }
        }
}



 먼저, main 함수의 황토색 부분을 보면, magicians 의 원소들을 뽑아내는데, magicians 라는 변수를 전혀 쓰지 않습니다. 물론, 내부적으로 iterator라는 변수가 magicians와 관계를 유지해주고 있긴합니다만, 일단 iterator를 가지고 온 후에는 데이터 집합체가 뭐냐에 신경을 쓸 필요가 없어진 거죠. iterator만 가져오면, 걍 hasNext() , next() 만 가지고 반복하면서 원소들에 대해서 처리를 하면 됩니다.

3. Iterator관련 interface

소스 코드의 보라색 부분이 jdk 안에 있는 Iterator에 관한 부분입니다.
java.util.Iterable 이란 넘을 구현하고 있습니다. 고놈한테는 Iterator<E> iterator() 라는 메소드 한개만 있습니다. 뭔소리냐면, 이 클래스는 무슨무슨 집합체 데이터를 가꾸 있으니깐, iterator로 원소들을 뽑아다가 쓸 수 있도록 제공하겠다는거죠.

그담에 등장하는 것이 java.util.Iterator입니다. 소스 코드의 청록색 부분입니다.
method가 3개가 있죠? hasNext()는 다음 구성 요소가 있냐고 물어봅니다. next()는 그 요소를 뽑아옵니다. remove()는 일부러 구현을 안했습니다. API에 보면, 마지막으로 꺼낸 요소를 제거한다.(optional operation) 이라고 되어있습니다. optional이라는 걸 강조할라고 구현 안했습니다. 그리고 iterator를 돌면서 데이터를 삭제한다는 것은 그다지 바람직해 보이진 않습니다.
요기서 한가지 집고 넘어가야 할 것은 시퀀스는 hasNext()가 아니라 next()에서 증가시켜야 한다는 것입니다. 좀 비상식적인 얘기긴 합니다만, hasNext()를 호출하고 또 호출하는 일이 발생할 수도 있기 때문이죠. hasNext라는 메소드 이름이, next를 가지고 있는지를 체크하겠다는 것이니까요.

4. JAVA API에 있는 Iterator

우리가 알고 있는 일반적인 집합체들은 전부 Iterator를 제공합니다. Set, List 등은 Collection 을 상속 받는데, Collection이 Iteratable을 상속 받기 때문입니다.
위에서 청록색 부분을 list.iterator() 라고 쭐여버려도 됩니다. 걍 있는 거 안 쓰고 굳이 구현한 건 예제 파일을 함 보여줄라고 한 겁니다. 사실은 예제 전체가 억지로 만들어낸 겁니다. 일반적인 집합체를 구현해서 쓰는 일은 거의 없고, JDK 안에 들어 있는 애들을 가져다 쓰는데, 걔들은 거의 대부분 Iterator를 제공하거든요.(Map은 한 다리 건너서 제공합니다.) 그래서 Iterator를 직접 구현할 일은 거의 없습니다. 가져다가 쓸 일이 있을 뿐이죠.

이제 Map은 왜 Iterator를 제공하지 않는 지를 살펴보죠. Map은 Set이나 List와는 달리 key-value의 구조입니다. key에 대한 Iterator인지 value에 대한 Iterator인지 구별할 방법이 없죠. 그래서 아예 제공을 안 합니다. 그러나 Map에는 key에 대해서는 Set<K> keySet()이라는 key를 Set으로 가져오기를 지원하고, value에 대해서는 Collection<V> values() 를 제공합니다. 위에서 말씀드렸다시피 Set과 Collection은 둘다 Iterator를 제공합니다.

5. Enumeration vs Iterator

둘은 굉장히 유사합니다. Enumeration의 경우는 boolean hasMoreElements() 와 E nextElement() 를 제공합니다. Iterator의 hasNext() , next() 에 대응되는 메쏘드들이죠.
차이는 두 가집니다. 첫째 Iterator에는 remove()가 있다. 둘째, Iterator의 함수 이름이 훨씬 쉽다.(타이핑 노가다가 쭐어든다.-_-; )
처음에 Enumeration이 나왔고, 그걸 쫌 편하게 만들어보자한 것이 Iterator랍니다.

'java' 카테고리의 다른 글

S-DES 소스 - java  (0) 2009.10.01
POI - 시트복사  (0) 2008.11.20
The Java XML Validation API - IBM developerworks  (0) 2008.09.19
Heap Dump 생성  (0) 2008.09.18
VM 분석 : Chapter 1 Performance - 2 / -Xrunprof 옵션  (0) 2008.09.17
Posted by marryjane
|


http://www-128.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html?ca=dnw-727#listing1

08 Aug 2006

Validation reports whether a document adheres to the rules specified by the schema. Different parsers and tools support different schema languages such as DTDs, the W3C XML Schema Language, RELAX NG, and Schematron. Java 5™ adds a uniform validation Application Programming Interface (API) that can compare documents to schemas written in these and other languages. Learn about this XML validation API.

Validation is a powerful tool. It enables you to quickly check that input is roughly in the form you expect and quickly reject any document that is too far away from what your process can handle. If there's a problem with the data, it's better to find out earlier than later.

In the context of Extensible Markup Language (XML), validation normally involves writing a detailed specification for the document's contents in any of several schema languages such as the World Wide Web Consortium (W3C) XML Schema Language (XSD), RELAX NG, Document Type Definitions (DTDs), and Schematron. Sometimes validation is performed while parsing, sometimes immediately after. However, it's usually done before any further processing of the input takes place. (This description is painted with broad strokes -- there are exceptions.)

Until recently, the exact Application Programming Interface (API) by which programs requested validation varied with the schema language and parser. DTDs and XSD were normally accessed as configuration options in Simple API for XML (SAX), Document Object Model (DOM), and Java™ API for XML Processing (JAXP). RELAX NG required a custom library and API. Schematron might use the Transformations API for XML(TrAX); and still other schema languages required programmers to learn still more APIs, even though they were performing essentially the same operation.

Java 5 introduced the javax.xml.validation package to provide a schema-language-independent interface to validation services. This package is also available in Java 1.3 and later when you install JAXP 1.3 separately. Among other products, an implementation of this library is included with Xerces 2.8.

Validation

The javax.xml.validation API uses three classes to validate documents: SchemaFactory, Schema, and Validator. It also makes extensive use of the javax.xml.transform.Source interface from TrAX to represent the XML documents. In brief, a SchemaFactory reads the schema document (often an XML file) from which it creates a Schema object. The Schema object creates a Validator object. Finally, the Validator object validates an XML document represented as a Source.

Listing 1 shows a simple program to validate a URL entered on the command line against the DocBook XSD schema.


Listing 1. Validating an Extensible Hypertext Markup Language (XHTML) document
import java.io.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.xml.sax.SAXException;

public class DocbookXSDCheck {

    public static void main(String[] args) throws SAXException, IOException {

        // 1. Lookup a factory for the W3C XML Schema language
        SchemaFactory factory = 
            SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        
        // 2. Compile the schema. 
        // Here the schema is loaded from a java.io.File, but you could use 
        // a java.net.URL or a javax.xml.transform.Source instead.
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
    
        // 3. Get a validator from the schema.
        Validator validator = schema.newValidator();
        
        // 4. Parse the document you want to check.
        Source source = new StreamSource(args[0]);
        
        // 5. Check the document
        try {
            validator.validate(source);
            System.out.println(args[0] + " is valid.");
        }
        catch (SAXException ex) {
            System.out.println(args[0] + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

Here's some typical output when checking an invalid document using the version of Xerces bundled with Java 2 Software Development Kit (JDK) 5.0:

file:///Users/elharo/CS905/Course_Notes.xml is not valid because cvc-complex-type.2.3: Element 'legalnotice' cannot have character [children], because the type's content type is element-only.

You can easily change the schema to validate against, the document to validate, and even the schema language. However, in all cases, validation follows these five steps:

  1. Load a schema factory for the language the schema is written in.
  2. Compile the schema from its source.
  3. Create a validator from the compiled schema.
  4. Create a Source object for the document you want to validate. A StreamSource is usually simplest.
  5. Validate the input source. If the document is invalid, the validate() method throws a SAXException. Otherwise, it returns quietly.

You can reuse the same validator and the same schema multiple times in series. However, neither class is thread-safe or reentrant. If you validate in multiple threads simultaneously, make sure each one has its own Validator and Schema objects.

Validate against a document-specified schema

Some documents specify the schema they expect to be validated against, typically using xsi:noNamespaceSchemaLocation and/or xsi:schemaLocation attributes like this:

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
  ...

If you create a schema without specifying a URL, file, or source, then the Java language creates one that looks in the document being validated to find the schema it should use. For example:

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();

However, normally this isn't what you want. Usually the document consumer should choose the schema, not the document producer. Furthermore, this approach works only for XSD. All other schema languages require an explicitly specified schema location.



Back to top


Abstract factories

SchemaFactory is an abstract factory. The abstract factory design pattern enables this one API to support many different schema languages and object models. A single implementation usually supports only a subset of the numerous languages and models. However, once you learn the API for validating DOM documents against RELAX NG schemas (for instance), you can use the same API to validate JDOM documents against W3C schemas.

For example, Listing 2 shows a program that validates DocBook documents against DocBook's RELAX NG schema. It's almost identical to Listing 1. The only things that have changed are the location of the schema and the URL that identifies the schema language.


Listing 2. Validating a DocBook document using RELAX NG
import java.io.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.xml.sax.SAXException;

public class DocbookRELAXNGCheck {

    public static void main(String[] args) throws SAXException, IOException {

        // 1. Specify you want a factory for RELAX NG
        SchemaFactory factory 
         = SchemaFactory.newInstance("http://relaxng.org/ns/structure/1.0");
        
        // 2. Load the specific schema you want. 
        // Here I load it from a java.io.File, but we could also use a 
        // java.net.URL or a javax.xml.transform.Source
        File schemaLocation = new File("/opt/xml/docbook/rng/docbook.rng");
        
        // 3. Compile the schema.
        Schema schema = factory.newSchema(schemaLocation);
    
        // 4. Get a validator from the schema.
        Validator validator = schema.newValidator();
        
        // 5. Parse the document you want to check.
        String input 
         = "file:///Users/elharo/Projects/workspace/CS905/build/Java_Course_Notes.xml";
        
        // 6. Check the document
        try {
            validator.validate(source);
            System.out.println(input + " is valid.");
        }
        catch (SAXException ex) {
            System.out.println(input + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

If you run this program with the stock Sun JDK and no extra libraries, you'll probably see something like this:

Exception in thread "main" java.lang.IllegalArgumentException: 
http://relaxng.org/ns/structure/1.0
	at javax.xml.validation.SchemaFactory.newInstance(SchemaFactory.java:186)
	at DocbookRELAXNGCheck.main(DocbookRELAXNGCheck.java:14)

This is because, out of the box, the JDK doesn't include a RELAX NG validator. When the schema language isn't recognized, SchemaFactory.newInstance() throws an IllegalArgumentException. However, if you install a RELAX NG library such as Jing and a JAXP 1.3 adapter, then it should produce the same answer the W3C schema does.

Identify the schema language

The javax.xml.constants class defines several constants to identify schema languages:

  • XMLConstants.W3C_XML_SCHEMA_NS_URI: http://www.w3.org/2001/XMLSchema
  • XMLConstants.RELAXNG_NS_URI: http://relaxng.org/ns/structure/1.0
  • XMLConstants.XML_DTD_NS_URI: http://www.w3.org/TR/REC-xml

This isn't a closed list. Implementations are free to add other URLs to this list to identify other schema languages. Typically, the URL is the namespace Uniform Resource Identifier (URI) for the schema language. For example, the URL http://www.ascc.net/xml/schematron identifies Schematron schemas.

Sun's JDK 5 only supports XSD schemas. Although DTD validation is supported, it isn't accessible through the javax.xml.validation API. For DTDs, you have to use the regular SAX XMLReader class. However, you can install additional libraries that add support for these and other schema languages.

How schema factories are located

The Java programming language isn't limited to a single schema factory. When you pass a URI identifying a particular schema language to SchemaFactory.newInstance(), it searches the following locations in this order to find a matching factory:

  1. The class named by the "javax.xml.validation.SchemaFactory:schemaURL" system property
  2. The class named by the "javax.xml.validation.SchemaFactory:schemaURL" property found in the $java.home/lib/jaxp.properties file
  3. javax.xml.validation.SchemaFactory service providers found in the META-INF/services directories of any available Java Archive (JAR) files
  4. A platform default SchemaFactory, com.sun.org.apache.xerces.internal.jaxp.validation.xs.SchemaFactoryImpl in JDK 5

To add support for your own custom schema language and corresponding validator, all you have to do is write subclasses of SchemaFactory, Schema, and Validator that know how to process your schema language. Then, install your JAR in one of these four locations. This is useful for adding constraints that are more easily checked in a Turing-complete language like Java than in a declarative language like the W3C XML Schema language. You can define a mini-schema language, write a quick implementation, and plug it into the validation layer.



Back to top


Error handlers

The default response from a schema is to throw a SAXException if there's a problem and do nothing if there isn't. However, you can provide a SAX ErrorHandler to receive more detailed information about the document's problems. For example, suppose you want to log all validation errors, but you don't want to stop processing when you encounter one. You can install an error handler such as that in Listing 3.


Listing 3. An error handler that merely logs non-fatal validity errors
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class ForgivingErrorHandler implements ErrorHandler {

    public void warning(SAXParseException ex) {
        System.err.println(ex.getMessage());
    }

    public void error(SAXParseException ex) {
        System.err.println(ex.getMessage());
    }

    public void fatalError(SAXParseException ex) throws SAXException {
        throw ex;
    }

}

To install this error handler, you create an instance of it and pass that instance to the Validator's setErrorHandler() method:

  ErrorHandler lenient = new ForgivingErrorHandler();
  validator.setErrorHandler(lenient);



Back to top


Schema augmentation

Some schemas do more than validate. As well as providing a true-false answer to the question of whether a document is valid, they also augment the document with additional information. For example, they can provide default attribute values. They might also assign types like int or gYear to an element or attribute. The validator can create such type-augmented documents and write them onto a javax.xml.transform.Result object. All you need to do is pass a Result as the second argument to validate. For example, Listing 4 both validates an input document and creates an augmented DOM document from the combination of the input with the schema.


Listing 4. Augmenting a document with a schema
import java.io.*;
import javax.xml.transform.dom.*;
import javax.xml.validation.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class DocbookXSDAugmenter {

    public static void main(String[] args) 
      throws SAXException, IOException, ParserConfigurationException {

        SchemaFactory factory 
         = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
        Validator validator = schema.newValidator();
        
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); // never forget this
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(new File(args[0]));
        
        DOMSource source = new DOMSource(doc);
        DOMResult result = new DOMResult();
        
        try {
            validator.validate(source, result);
            Document augmented = (Document) result.getNode();
            // do whatever you need to do with the augmented document...
        }
        catch (SAXException ex) {
            System.out.println(args[0] + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

This procedure can't transform an arbitrary source into an arbitrary result. It doesn't work at all for stream sources and results. SAX sources can be augmented into SAX results, and DOM sources into DOM results; but SAX sources can't be augmented to DOM results or vice versa. If you need to do that, first augment into the matching result -- SAX for SAX and DOM for DOM -- and then use TrAX's identity transform to change the model.

This technique isn't recommended, though. Putting all the information the document requires in the instance is far more reliable than splitting it between the instance and the schema. You might validate, but not everyone will.



Back to top


Type information

The W3C XML Schema Language is heavily based on the notion of types. Elements and attributes are declared to be of type int, double, date, duration, person, PhoneNumber, or anything else you can imagine. The Java Validation API includes a means to report such types, although it's surprisingly independent of the rest of the package.

Types are identified by an org.w3c.dom.TypeInfo object. This simple interface, summarized in Listing 5, tells you the local name and namespace URI of a type. You can also tell whether and how a type is derived from another type. Beyond that, understanding the type is up to your program. The Java language doesn't tell you what it means or convert the data to a Java type such as double or java.util.Date.


Listing 5. The DOM TypeInfo interface
package org.w3c.dom;

public interface TypeInfo {

  public static final int DERIVATION_RESTRICTION;
  public static final int DERIVATION_EXTENSION;
  public static final int DERIVATION_UNION;

  public String  getTypeName();
  public String  getTypeNamespace()
  public boolean isDerivedFrom(String namespace, String name, int derivationMethod);

}

To get TypeInfo objects, you ask the Schema object for a ValidatorHandler rather than a Validator. ValidatorHandler implements SAX's ContentHandler interface. Then, you install this handler in a SAX parser.

You also install your own ContentHandler in the ValidatorHandler (not the parser); the ValidatorHandler will forward the augmented events on to your ContentHandler.

The ValidatorHandler makes available a TypeInfoProvider that your ContentHandler can call at any time to find out the type of the current element or one of its attributes. It can also tell you whether an attribute is an ID, and whether the attribute was explicitly specified in the document or defaulted in from the schema. Listing 6 summarizes this class.


Listing 6. The TypeInfoProvider class
package javax.xml.validation;

public abstract class TypeInfoProvider {

  public abstract TypeInfo getElementTypeInfo();
  public abstract TypeInfo getAttributeTypeInfo(int index);
  public abstract boolean  isIdAttribute(int index);
  public abstract boolean  isSpecified(int index);

}

Finally, you parse the document with the SAX XMLReader. Listing 7 shows a simple program that uses all these classes and interfaces to print out the names of all the types of the elements in a document.


Listing 7. Listing element types
import java.io.*;
import javax.xml.validation.*;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class TypeLister extends DefaultHandler {

    private TypeInfoProvider provider;
    
    public TypeLister(TypeInfoProvider provider) {
        this.provider = provider;
    }

    public static void main(String[] args) throws SAXException, IOException {

        SchemaFactory factory 
         = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
    
        ValidatorHandler vHandler = schema.newValidatorHandler();
        TypeInfoProvider provider = vHandler.getTypeInfoProvider();
        ContentHandler   cHandler = new TypeLister(provider);
        vHandler.setContentHandler(cHandler);
        
        XMLReader parser = XMLReaderFactory.createXMLReader();
        parser.setContentHandler(vHandler);
        parser.parse(args[0]);
        
    }
    
    public void startElement(String namespace, String localName,
      String qualifiedName, Attributes atts) throws SAXException {
        String type = provider.getElementTypeInfo().getTypeName();
        System.out.println(qualifiedName + ": " + type);
    }

}

Here's the start of the output from running this code on a typical DocBook document:

book: #AnonType_book
title: #AnonType_title
subtitle: #AnonType_subtitle
info: #AnonType_info
copyright: #AnonType_copyright
year: #AnonType_year
holder: #AnonType_holder
author: #AnonType_author
personname: #AnonType_personname
firstname: #AnonType_firstname
othername: #AnonType_othername
surname: #AnonType_surname
personblurb: #AnonType_personblurb
para: #AnonType_para
link: #AnonType_link

As you can see, the DocBook schema assigns most elements anonymous complex types. Obviously, this will vary from one schema to the next.



Back to top


Conclusion

The world would be a poorer place if everyone spoke just one language. Programmers would be unhappy if they had only one programming language to choose from. Different languages suit different tasks better, and some tasks require more than one language. XML schemas are no different. You can choose from a plethora of useful schema languages. In Java 5 with javax.xml.validation, you have an API that can handle all of them.


'java' 카테고리의 다른 글

POI - 시트복사  (0) 2008.11.20
자바 디자인 패턴 1 - Iterator  (0) 2008.09.24
Heap Dump 생성  (0) 2008.09.18
VM 분석 : Chapter 1 Performance - 2 / -Xrunprof 옵션  (0) 2008.09.17
java.lang.OutOfMemoryError - OOME  (0) 2008.09.17
Posted by marryjane
|

Heap Dump 생성

java 2008. 9. 18. 10:59

http://blog.naver.com/clotho95/140048453696

1. Sun JDK

 

  1) HPRof 사용

      JDK 버전에 따라 JVM 옵션에 -Xrunhprof:heap=dump,format=b,doe=n (JDK 1.4) 또는 -agentlib:hprof=heap=dump,format=b,doe=n (JDK 1.5)을 명시하고, 실행시에 Control-break 또는 kill -3 <pid> 명령으로 heap dump를 생성할 수 있다. Windows 환경이라면 SendSignal.exe를 사용하면 된다.

      하지만 오버헤드가 너무 커서 운영환경에서 사용하기에는 무리가 따른다. 이 옵션없이 JBoss를 기동하면 30초면 뜨는데, 옵션을 키면 무려 3분 30초가 걸렸다.

 

      * 설정

        - -Xrunhprof:heap=dump,format=b,doe=n (JDK 1.4)

        - -agentlib:hprof=heap=dump,format=b,doe=n (JDK 1.5)

      * heap dump 생성

        - kill -3 <pid>

        - SendSignal.exe <pid>

 

   2) OutOfMemoryError 발생 시 자동으로 heap dump

      JVM 옵션에 -XX:+HeapDumpOnOutOfMemoryError를 설정하면 OutOfMemoryError 발생 시에 heap dump를 현재 실행 디렉터리에 생성한다. JBoss의 경우는 $JBOSS_HOME/bin 디렉터리이다. heap dump가 생성되는 경로를 지정하려면 -XX:HeapDumpPath를 설정해준다.

      Sun JDK 1.4.2_12, 1.5.0_7, 그리고 HP 1.4.2_11 이후 버전에서 지원된다.

 

      * 설정

         - -XX:+HeapDumpOnOutOfMemoryError

         - -XX:HeapDumpPath=/path

       * heap dump 생성

         - OutOfMemoryError 발생시

 

   3) SIGQUIT (kill -3 <pid>)

      JVM 옵션에 -XX:HeapDumpOnCtrlBreak를 설정하면 SIGQUIT signal로 heap dump를 생성할 수 있다. JDK 1.5.0_14 부터 지원된다. 하지만, Windows와 Linux 버전의 JDK 1.5.0_14는 버그로 인해 "/"가 포함된 파일명으로 heap dump를 생성하려고해 No such file or directory라는 에러를 내며 heap dump가 생성되지 않는다. Solaris 버전은 테스트를 못해봤는데 설마 동작하지 않을까 싶다.

 

       * 설정

         - -XX:HeapDumpOnCtrlBreak

       * heap dump 생성

         - kill -3 <pid>

         - SendSignal.exe <pid>

 

   4) jmap 사용

      별도로 JVM 옵션에 명시할 필요없이 JDK에 포함되어 있는 jmap을 사용해 heap dump를 생성할 수 있다. Windows 버전은 JDK 1.5 버전에 jmap이 포함되어 있지 않으므로 사용할 수 없다.

 

       * heap dump 생성

         - jmap -heap:format=b <pid> (JDK 1.5)

         - jmap -dump:format=b,file=<filename> <pid> (JDK 6)

 

2. HP JDK

 

    1)  SIGQUIT (kill -3 <pid>)

        HP의 경우 JVM 옵션에 -XX:+HeapDump를 설정해 SIGQUIT signal로 heap dump를 생성할 수 있다. 기본적으로 text 형식으로 저장되는데 binary 형식이 필요하면 _JAVA_BINARY_HEAPDUMP 환경변수를 설정하면 된다. 이 방법 외에도 Sun JDK처럼 -XX:+HeapDumpOnCtrlBreak를 설정하면 binary heap dump를 생성한다. JVM 옵션을 변경하기 싫으면 _JAVA_HEAPDUMP 환경변수를 설정해도 된다.

       * 설정

         - -XX:+HeapDump 또는 -XX:HeapDumpOnCtrlBreak

         - export _JAVA_HEAPDUMP=1

       * heap dump 생성

        - kill -3 <pid>

 

     2) OutOfMemoryError 발생 시 자동으로 heap dump 생성

        Sun JDK처럼 JVM 옵션에 -XX:+HeapDumpOnOutOfMemoryError를 설정하면 OutOfMemoryError 발생 시에 heap dump를 현재 실행 디렉터리에 생성한다.

 

 

참고자료

http://java.sun.com/javase/6/webnotes/trouble/other/troubleshooting-j2se5.html

http://java.sun.com/javase/6/webnotes/trouble/

http://docs.hp.com/en/5992-1918/ch01s27.html

http://www.latenighthacking.com/projects/2003/sendSignal/

http://forum.java.sun.com/thread.jspa?threadID=681590&messageID=3975318

http://blogs.sun.com/alanb/entry/heap_dumps_are_back_with

Posted by marryjane
|

http://blog.empas.com/flag2ejb/4941495

-Xrunprof 옵션
이 옵션은 단순히 텍스트 형태로 쓰레드 정보와 메소드 호출 점유율, 호출 횟수, 호출 쓰레드 번호등을 보여준다. 이 옵션은 어플리케이션의 성능 측정에서 병목점을 찾을 때 많이 사용한다.
병목점은 찾는 방법은 점유율(%)가 높으면서, 호출 횟수는 작은 순서가 병목점의 순서가 된다.
최근에는 이 옵션은 단순히 HAT(The Heep Analysis Tool)을 실행하기 위한 바이너리 타입의 기초 데이터를 생성(format=b 옵션)하는 용도로 많이 쓰이기도 한다.
 
[craftlee@hurukku testspace]$ java -Xrunhprof:help
Hprof usage: -Xrunhprof[:help]|[:<option>=<value>, ...]
 
Option Name and Value  Description                Default
---------------------  ----------------------     -------
heap=dump|sites|all    heap profiling             all
cpu=samples|times|old  CPU usage                  off
monitor=y|n            monitor contention         n
format=a|b             ascii or binary output     a
file=<file>            write data to file         java.hprof(.txt for ascii)
net=<host>:<port>      send data over a socket    write to file
depth=<size>           stack trace depth          4
cutoff=<value>         output cutoff point        0.0001
lineno=y|n             line number in traces?     y
thread=y|n             thread in traces?          n
doe=y|n                dump on exit?              y
gc_okay=y|n            GC okay during sampling    y
 
Example: java -Xrunhprof:cpu=samples,file=log.txt,depth=3 FooClass
 
Note: format=b cannot be used with cpu=old|times
 
일반적인 사용 옵션
-         java -Xrunhprof <class>
-         java -Xrunhprof:cpu=samples,thread=y <class>
-         java -Xrunhprof:heap=sites <class>
-         java -Xrunhprof:cpu=samples <class>
-         java -Xrunhprof:cpu=samples,file=log.txt <class>
-         java -Xrunhprof:cpu=samples,format=b <class>
 
실행 예) 위에서 만든 “wileUnit.class”을 이용한다.
 
1.       java -Xrunhprof:cpu=samples,thread=y whileUnit 옵션으로 실행
THREAD START (obj=811a410, id = 1, name="Finalizer", group="system")
THREAD START (obj=811a530, id = 2, name="Reference Handler", group="system")
THREAD START (obj=811a5f8, id = 3, name="main", group="main")
THREAD START (obj=813c9c8, id = 4, name="Signal Dispatcher", group="system")
THREAD START (obj=813ca58, id = 5, name="HPROF CPU profiler", group="system")
THREAD END (id = 3)
THREAD START (obj=814daf8, id = 6, name="DestroyJavaVM", group="main")
THREAD END (id = 6)
TRACE 1: (thread=3)
        <empty>
TRACE 2: (thread=3)
        whileUnit.exec(whileUnit.java:15)
        whileUnit.main(whileUnit.java:27)
CPU SAMPLES BEGIN (total = 2) Mon Nov 22 12:59:32 2004
rank   self  accum   count trace method
   1 100.00% 100.00%       2     2 whileUnit.exec
CPU SAMPLES END
2.       위에서는 실행 메소드가 하나 뿐이 없어서 100%을 차지 하고있다.
3.       java -Xrunhprof:heap=sites whileUnit 옵션으로 실행
 
percent         live       alloc'ed  stack class
 rank   self  accum    bytes objs   bytes objs trace name
    1 63.90% 63.90%   602072  234  602072  234     1 [I
    2 16.32% 80.23%   153800  577  153800  577     1 [C
    3  5.02% 85.25%    47280  230   47280  230     1 [B
….
메모리에 점유율이 높은 객체 Ranking이 출력된다. 여기서 [I, [C, [B 타입은 Integer, Character, Byte 배열이다.
Posted by marryjane
|

http://ukja.tistory.com/tag/Java.OutOfMemoryError

사내에서 하는 프로젝트의 일환으로 Java의 성능 이슈에 대한 다양한 컨텐츠를 작성 중인데... 그 중 유용하다고 생각되는 내용 일부를 블로그를 통해서 공개할 생각이다. 그 첫번째 내용으로 악명높은 Out Of Memory Error, 흔히 OOM(E)라고 부르는 현상의 원인과 해결책에 대해 정리한 문서를 소개한다...


OOME 개요

JVM이 일정한 크기의 메모리를 할당하는데 실패하면 Out Of Memory Error, 이른바 OOME가 발생한다.

OOME의 발생 원인은 매우 다양하며, 이는 JVM이 사용하는 메모리 공간의 다양성에 기인한다. 대부분의 JVM은그 사용 용도에 따라 메모리를 몇가지 종류로 구분해서 사용한다. 가령 Sun HotSpot JVM은 다음과 같은 세 가지 종류의메모리 공간을 사용한다.((참고) 통상적으로 Permanent Space는 Java Heap의 하위 영역으로 설명된다. 하지만 본 문서에서는 Java Heap = Young Generation + Old Generation으로 간주한다)

  1. Java Heap: 사용자가 생성하는 Java Object들이 거주하는 공간이다. -Xms와 -Xmx Option에 의해 크기가 결정된다.
  2. Permanent Space: Class에 대한 메타 정보를 저장하는 공간이다. -XX:PermSize=와 -XX:MaxPermSize= Option에 의해 크기가 결정된다.
  3. Native Heap: Java Object가 아닌 Native Object들이 거주하는 공간이다. Native Heap의 크기는 JVM Option으로 지정할 수 없으며, OS 차원에서 결정된다.


각 메모리 공간의 용도와 사용 방식이 틀리기 때문에 OOME또한 매우 다양한 상황에서 발생하게 된다. OOME가 발생하는 정확한 원인을 분석하려면 각 메모리 공간의 특성을 이해하고 그에 맞는 해결책을 모색해야 한다.

(주의) 비록 Java 언어와 JVM이 자동화된 메모리 관리 기능을 제공하지만, 이것이개발자나 관리자가 메모리 관리에 대해 무신경해도 된다는 것을 의미하지 않는다는 사실을 명심하자. Java에서도 잘못된 메모리관리는 여전히 많은 문제를 일으키며, Garbage Collection에 의한 성능저하나 OOME에 의한 Applictaion정지나 System Crash등이 대표적인 예이다.

Java Heap에서의 OOME

Java Heap에서 OOME가 발생하는 경우에는 다음과 같은 에러 메시지가 출력된다.

Exception in thread "main": java.lang.OutOfMemoryError: Java heap space 또는
Exception in thread main: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

전자의 메시지는 Java Heap Space의 부족으로 Object를 생성하지 못하는 경우에 발생한다. 후자의메시지는 Java Heap의 최대 크기보다 큰 Array가 요청되는 경우에 발생한다. 가령 Java Heap의 최대 크기가256M인 상황에서 300M 크기의 Array를 생성하는 경우가 이에 해당한다.

Java Heap에서 OOME가 발생하는 이유는 다음과 같다.

  • Java Heap의 크기가 작은 경우
  • Memory Leak이 발생하는 경우
    • Application Logic에 의한 Memory Leak
    • JDK Bug나 WAS Bug에 의한 Memory Leak
  • finalize 메소드에 의한 Collection 지연


Java Heap의 크기와 OOME

Java Heap의 최대 크기가 Application의 메모리 요구량에 비해 작게 설정된 경우에 OOME가 발생한다.Memory Leak이 발생하지 않는데도 OOME가 발생한다면 Java Heap의 크기가 부족하다고 판단할 수 있다.-Xmx 옵션을 이용해서 Java Heap의 최대 크기를 키워주어야 한다.

Memory Leak과 OOME

Memory Leak이발생하는 경우에는 Java Heap의 크기와 무관하게 OOME가 발생할 수 있다. 아무리 Java Heap의 크기를 크게하더라도 결국 Memory Leak에 의해 Collection되지 않는 Garbage 객체들이 메모리를 다 소진하기 때문이다.Memory Leak은 대부분 잘못된 Application 로직에 의해 발생한다. Object에 대한 참조(Reference)관계가 복잡한 경우 조그마한 실수로 인해 사용되지 않은 Object를 계속해서 참조하게 된다. 이러한 Object들은 비록Application에서는 사용되지 않지만 Garbage Collection에 의해 메모리 해제가 이루어지지 않기 때문에OOME를 유발하게 된다.

JDK Bug나 WAS Bug에 의해서도 Memory Leak이 발생할 수 있다. JDK가 제공하는 라이브러리나WAS가 제공하는 라이브러리에서 로직 오류로 인한 Memory Leak 가능성이 있기 때문이다. ApplicationLogic에서 Memory Leak이 검출되지 않는 경우에는 JDK나 WAS의 Bug를 의심해볼 필요가 있으며 각 Vendor가제공하는 Bug Database를 통해 검색 가능하다.

finalize 메소드에 의한 Collection 지연과 OOME

특정 Class에 finalize 메소드가 정의되어 있는 경우, 이 Class Type의 Object는 GarbageCollection 발생시 즉각적으로 Collection 되지 않는다. 대신 Finalization Queue에 들어간 후Finalizer에 의해 정리가 된다. Finalizer는 Object의 finalize 메소드를 실행한 후 메모리 정리 작업을수행한다. 만일 finalize 메소드를 수행하는데 오랜 시간이 걸린다면 그 만큼 객체가 오랫동안 메모리를 점유하게 된다. 이로인해 OOME가 발생할 확률이 높아진다. 이런 이유 때문에 finalize 메소드는 되도록 사용하지 말아야 한다.

Object Allocation Profiling

Java Heap의 메모리 부족 문제를 정확하게 분석하려면 Object Allocation Profiling을 수행해야한다. 즉, 어떤 Object가 어느 개수만큼 생성되었으며 얼마나 많은 메모리를 차지하는지 분석할 필요가 있다. HProf는 모든 JVM이 표준으로 제공하는 Profiler로, 간단한 Object Allocation Profiling 기능을 제공한다.

 java -Xrunhprof:heap=sites [Main Class] 

또는 다음과 같이 doe(dump on exit) Option을 비활성화해서 시간순으로 Profiling을 수행할 수도 있다.

 java -Xrunhprof:heap=sites,doe=n [Main Class]
...
Control+Break (혹은 다른 Console에서 kill -3 [pid])

Java Process에서 Signal을 보내서 Dump를 생성하는 방법은 Thread Dump를 참조한다.

아래에 HProf를 이용한 Object Allocation Profiling 결과의 간단한 Sample이 있다. 아래 Sample에서는 byte[] 유형의 객체가 20%의 Heap 공간을 사용하는 것을 알 수 있다.

        percent          live          alloc'ed  stack class
rank self accum bytes objs bytes objs trace name
1 20.36% 20.36% 190060 16 190060 16 300000 byte[]
2 14.92% 35.28% 139260 1059 139260 1059 300000 char[]
3 5.27% 40.56% 49192 15 49192 15 300055 byte[]
4 5.26% 45.82% 49112 14 49112 14 300066 byte[]
5 4.32% 50.14% 40308 1226 40308 1226 300000 java.lang.String
6 1.62% 51.75% 15092 438 15092 438 300000 java.util.HashMap$Entry
7 0.79% 52.55% 7392 14 7392 14 300065 byte[]
8 0.47% 53.01% 4360 16 4360 16 300016 char[]
9 0.47% 53.48% 4352 34 4352 34 300032 char[]
10 0.43% 53.90% 3968 32 3968 32 300028 char[]
11 0.40% 54.30% 3716 8 3716 8 300000 java.util.HashMap$Entry[]
12 0.40% 54.70% 3708 11 3708 11 300000 int[]

Permanent Space에서의 OOME

Permanent Space에서 OOME가 발생하면 다음과 같은 에러 메시지가 출력된다

Exception in thread "main": java.lang.OutOfMemoryError: Perm Gen space'

Permanent Space는 Class의 메타 정보를 저장하는 공간이다. 따라서 많은 수의 Class를 로딩하는Application의 경우 Permanent Space의 크기가 작으면 OOME가 발생할 수 있다. 다음과 같은 유형의Application들에서는 Permanent Space의 크기를 키워줄 필요가 있다.

  • 매우 많은 수의 JSP 파일을 로딩하는 Web Application. JSP 파일은 Servlet으로 변환된다.하나의 Servlet은 하나의 Java Class에 해당한다. 따라서 이 경우 매우 많은 수의 Class가 로딩된다.
  • ReflectionMechanism을 사용해 동적으로 Class를 로딩하는 Framework. Spring과 같은 현대적인 Framework들은Reflection Meachanism을 통해 동적으로 Class를 생성한다. 이 경우 개발자가 의도하지 않은 많은 수의Class들이 로딩될 수 있다.

이런 문제는 대부분의 Permanent Space의 크기를 키워주면 해결된다.-XX:PermSize=, -XX:MaxPermSize= Option을 이용해Permanent Space의 최소 크기와 최대 크기를 지정할 수 있다.

Class Loading 모니터링

Permanent Space에 Loading되는 Class의 목록을 모니터링함으로써 OOME가 발생하는 원인을 간접적으로 분석할 수 있다. 다음과 같은 방법을 통해 Class Loading을 모니터링할 수 있다.

  • -verbose:gc: Loading되는 Class들을 Standard Out을 통해 출력해준다.
  • Platform MBean: JMX 표준을 통해 제공되는 ClassLoadingMXBean API를 이용하면 프로그래밍적으로 Class Loading 정보를 얻을 수 있다.
  • JConsole: JConsole을 이용하면 Class Loading 정보를 조회할 수 있다. JConsole은 JMX 클라이언트의 표준 샘플로 Platform MBean과 통신해서 Class Loading 정보를 얻는다.

아래에 -verbose:class 옵션에 의한 Class Loading 모니터링의 간단한 Sample이 있다. Open된 jar 파일과 Loading된 Class 목록을 확인할 수 있다.

[Opened c:bea10jdk150_06jrelibrt.jar]
[Opened c:bea10jdk150_06jrelibjsse.jar]
[Opened c:bea10jdk150_06jrelibjce.jar]
[Opened c:bea10jdk150_06jrelibcharsets.jar]
[Loaded java.lang.Object from c:bea10jdk150_06jrelibrt.jar]
[Loaded java.io.Serializable from c:bea10jdk150_06jrelibrt.jar]
[Loaded java.lang.Comparable from c:bea10jdk150_06jrelibrt.jar]
[Loaded java.lang.CharSequence from c:bea10jdk150_06jrelibrt.jar]
[Loaded java.lang.String from c:bea10jdk150_06jrelibrt.jar]
[Loaded java.lang.reflect.GenericDeclaration from c:bea10jdk150_06jrelibrt.jar]
...

(참조) IBM JVM에서는 Thread Dump에서도 Class Loading 정보를 제공한다.

Class Reloading과 OOME

현대적인 대부분의 WAS가 Class Reloading 기능을 제공한다. Class Reloading이란 Runtime에Class가 재생성되면 이를 JVM을 Reboot하지 않고 Reloading하는 기능을 의미한다. 일부 WAS의 경우Class가 Reloading될 때 이전 버전의 Class를 해제하지 않는 경우가 있다. 따라서 Class Reloading이자주 발생하면 Permanent Space가 금방 꽉차게 되고 OOME가 발생하게 된다. 이와 같은 경우에는 WAS가 제공하는버그 패치를 적용하거나 WAS를 주기적으로 Restart해야 한다.

Native Heap에서의 OOME

Java Heap과 Permanent Space가 Java와 관련된 Object들이 거주하는 공간인 반면, NativeHeap은 OS 레벨의 Native Object나 JNI Library 레벨의 Native Object이 거주하는 공간이다.

Native Heap에서 OOME가 발생하면 다음과 같은 에러 메시지가 출력된다.

java.lang.OutOfMemoryError: request bytes for . Out of swap space? 또는
java.lang.OutOfMemoryError: (Native method)' 또는
java.lang.OutOfMemoryError: unable to create new native thread


첫번째 메시지는 VM code 레벨에서 메모리 부족 현상이 발견된 경우이다. 두번째 메시지는 JNI나 Native Method에서 메모리 부족 현상이 발견된 경우에 해당한다. 세번째 메시지는 Thread를 생성할 수 없을 때 발생한다. Thread는Native Heap 공간의 메모리를 필요로 하기 때문에 Native Heap 공간의 메모리가 부족하면 Thread 생성시OOME가 발생한다.

Native Heap에서 메모리 부족이 발생하는 이유는 매우 다양하다.

  • Thread Stack Space가 부족한 경우
  • Virtual Space Address가 소진된 경우
  • Swap Space가 모자란 경우
  • JNI Library에서 Memory Leak이 발생하는 경우

Thread Stack Space와 OOME

Java Thread는 Native Heap 공간에 Stack Trace를 저장할 공간을 필요로 한다. ThreadStack Space의 크기는 -Xss 옵션을 통해 지정된다. -Xss 옵션을 통해지정되는 공간은 개별 Thread가 사용하는 공간이다. 만일 N개의 Thread가 활성화되면 N* 만큼의메모리 공간이 필요하다.

대부분의 OS에서 Thread Stack Size는 512K ~ 1M 사이다. 따라서 많은 수의 Thread가 활성화되면 Thread Stack Space만으로도 큰 크기의 Native Heap 메모리 공간을 소모한다.

Thread Stack Space 문제에 의한 OOME를 해소하는 방법은 다음과 같다.

  • Thread의 수를 줄인다. 동시에 수십개 이상의 Thread를 사용하는 것은 메모리의 문제 뿐만 아니라 지나친 Context Switching으로 인해 성능을 저하시키는 요인이 된다. Thread Pool 기법을 사용해서 동시 Thread의 수를 줄인다. 대부분의 WAS들이 Thread Pool 기법을 사용하고 있다.
  • Thread Stack Size를 줄인다. 대부분의 OS에서 Thread Stack Size는512K ~ 1M이다. 만일 많은 수의 Thread가 필요한 Application이라면 Thread Stack Size를줄임으로써 OOME를 방지할 수 있다. 많은 경우 -Xss128k 정도나 -Xss256k 정도의 크기에서도 문제없이 작동한다.단, Stack Size가 줄어든 만큼 Stack Overflow Error가 발생할 확률은 높아진다.
  • Java Heap 크기를 줄인다. 32bit Process가 사용 가능한 메모리 공간은 OS에따라 2G ~ 4G로 제한된다. 하나의 Java Process가 사용 가능한 공간은 [Java Heap+PermanentSpace+Native Heap]으로 이루어진다. 따라서 Java Heap이 지나치게 큰 공간을 사용하는 경우 NativeHeap에서 사용 가능한 공간이 줄어들게 된다. 따라서 Java Heap 크기를 줄이면 Native Heap의 메모리 부족에의한 OOME 문제를 해결 할 수 있다. 하지마 Java Heap 크기를 지나치게 줄이면 Java Heap 부족에 의한 OOME현상이 발생할 수 있으므로 유의해야 한다. Java Heap 크기를 줄이는 방법은 Thread Stack Space의 부족 문제뿐 아니라 Native Heap 부족에 의한 OOME 문제를 줄이는 공통적인 해결 방법이다.
  • 64bit JVM을 사용한다. 64bit JVM에서는 32bit JVM Process가 가지는2G ~ 4G의 제한이 없다. 따라서 Native Heap의 메모리 부족 문제가 줄어든다. 이 방법 또한 Native heap부족에 의한 OOME 문제를 줄이는 공통적인 해결 방안이다.

64bit JVM을 사용하는 경우, 다음과 같은 사실에 유의해야 한다.

  1. 일반적으로 32bit Application의 성능이 64bit Application에 비해 더 나은 성능을보이는 경우가 많다. 따라서 64bit JVM을 사용하는 경우 약간의 성능 저하가 발생할 수 있다는 사실에 유의해야 한다.
  2. 과도한 Virutal Memory의 사용은 Application의 성능을 저하시키는 주요인이다. JavaApplication의 성능은 모든 Object들이 Physical Memory에 머물러 있을때 가장 뛰어나다. 만일Physical Memory를 초과하는 크기의 Virtual Memory를 사용하면 Physical Memory의 일부를Disk에 저장하는 Paging In/Out이 발생한다. Paging In/Out은 Memory Operation에 비해 매우느리며 그만큼 Application의 성능도 저하된다.


Virtual Address Space와 OOME

32bit JVM에서 사용가능한 Virtual Address Space의 최대 크기는 OS에 따라 2G ~ 4G로제한된다. Java Process의 경우 Java Heap과 Permanent Space를 제외한 나머지 공간만을 NativeHeap이 사용할 수 있다. 가령 2G의 Virtual Address Space만이 사용가능하다고 가정하자. 이 때 JavaHeap이 1G, Permanent Space가 200M를 사용한다면 Native Heap이 사용 가능한 최대 크기는 800M에불과하다. 800M 중에는 OS가 Process 관리를 위해 사용하는 기본 메모리가 포함되기 때문에 실제로 JavaApplication이 사용 가능한 Native Heap의 크기는 훨씬 줄어든다. 따라서 이 경우 Native Heap 공간부족에 의한 OOME가 발생할 확률이 높아진다.

Virtual Address Space 부족에 의한 OOME를 해결하는 방법은 Thread Stack Space에의한 OOME 해결방안과 일맥상통한다. Java Heap 크기를 줄이거나 64bit JVM를 사용한다. Thread 수를줄이거나 Thread Stack Size를 줄임으로써 Native Heap 공간을 확보하는 것도 방법이 될 수 있다.

Swap Sapce와 OOME

Physical Memory를 초과하는 Virtual Memory 요청이 발생하면 Paging In/Out을 통해 필요한 메모리를 확보한다. Paging In/Out을 위한 공간이 Swap 공간이다. 따라서 Swap Space가 부족하면 Paging In/Out에 실패하고 이로 인해 OOME가 발생한다.

여러 개의 Process가 Swap Space를 사용하는 경우 Swap Space 부족에 의한 OOME가 발생할확률이 높아진다. OS가 제공하는 툴들을 통해 Swap Space와 Paging In/Out을 모니터링해야 하며, SwapSpace가 부족한 경우에는 크기를 키워주어야 한다.

OOME와 Fatal Error Log

Native Heap에서 OOME가 발생하면 JVM은 심각한 상황이라고 판단하고 Fatal Error Log를 남긴다. 아래에 OOME가 발생한 상황에서의 Fatal Error Log의 Header 정보에 대한 간단한 Sample이 있다.

#
# An unexpected error has been detected by Java Runtime Environment:
#
# java.lang.OutOfMemoryError: requested 20971520 bytes for GrET in C:BUILD_AREA
jdk6_02hotspotsrcsharevmutilitiesgrowableArray.cpp. Out of swap space?
#
# Internal Error (414C4C4F434154494F4E0E494E4C494E450E4850500017), pid=5840, ti
d=5540
#
# Java VM: Java HotSpot(TM) Client VM (1.6.0_02-b06 mixed mode)
# An error report file with more information is saved as hs_err_pid5840.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#

Fatal Error Log를 통해 OOME를 유발한 Thread와 Stack Trace를 추적할 수 있다. 이 정보가 곧 해결책을 의미하지는 않지만, 추가적인 분석을 위한 힌트가 될 수 있다.

Posted by marryjane
|

http://www.javaservice.net/~java/bbs/read.cgi?m=etc&b=jdk&c=r_p&n=1117521098&p=1&s=t#1117521098

java.lang.OutOfMemoryError

 

                     2005-05-31     송학렬    ㈜아이티플러스 기술지원부

 

 

이 문서는 기술지원 또는 개발 시 java.lang.OutOfMemoryError 를 만났을 때 원인 파악 및 해결방안 입니다.

 

 

java.lang.OutOfMemoryError 에는 크게 2가지 패턴이 있다고 볼 수 있습니다.(전적으로 개인적인 생각이지만..^^)

Java의 heap 메모리가 정말로 Full 되서 나는 종류가 있고 그렇지 않은데도 나는 종류가 있습니다.

그 둘 중에 어는 것 인지부터 가려내는 것이 가장 먼저 선행되어야 합니다.

Java 가상머신에는 메모리 구조가 여러단계로 나뉘어 져 있으므로 어느 영역에 의해 java.lang.OutOfMemoryError 가 나는지 알기 위해 JAVA OPTION으로 싸이트가 안정화 되기 전까진 verbosegc 또는 -XX:+PrintGCDetails  옵션을 추가해 놓는 것이 java.lang.OutOfMemoryError 났을 때를 대비해 좋은 습관이라 할 수 있습니다.

 

-verbosegc 또는  -XX:+PrintGCDetails  옵션으로 로그에 남는 heap 메모리 정보를 본 후 java.lang.OutOfMemoryError 가 날 때 heap이 꽉 차서 나는 것인지 아닌지 부터 판단합니다.

 

1.     Heap Memory가 Full 차지 않았을 때

1)     Perm 영역이 full 되는 경우

 

Permanent generation is full...

increase MaxPermSize (current capacity is set to: 134217728 bytes)

 

[Full GC[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor31282]

 810207K->802132K(1013632K), 8.3480617 secs]

<GC: 2 4  2465625.831280 10348 0 31 113802808 105534632 286326784 0 0 35782656 715849728 715848840 715849728 134217720 134023296 134217728 8.348677 8.348677 >

Passwd Check =============

<2005. 5. 19. 오전 9시 32분 23초 KST> <Error> <HTTP> <BEA-101017> <[ServletContext(id=2536415,name=/,context-path=)] Root cause of ServletException.

java.lang.OutOfMemoryError

위와 같은 case 인 경우네는 Java 가상머신중에 Perm 영역이 full 차는 경우 입니다. 

Perm 영역에는 class object 및 관련된 meta data가 로드되는 곳인데 싸이트가 매우 많은 수의 class를 사용하는 경우 늘려줘야 하는 case도 있습니다.

얼마가 적정한 사이즈란 정답이 없는 것 같고 max가 계속 늘어나지 않고 일정한 사이즈를 유지 하면 됩니다.

 

             위 에러가 났을때는 -XX:MaxPermSize=256m  옵션으로 사이즈를 적당하게 늘려주는게 방

             법이며 늘려주었는데도 불구하고 Full 차는 시간만 늘어날 뿐 계속 사이즈가 늘어난다면 이 영역은 일반 비즈니스 프로그램으로 핸들링 할 수 없는 영역이므로 WAS 제품의 버그 및 jdk 버그로 보는 것이 일반적입니다.

       

2)     MAXDSIZ 사이즈 관련

  - Exception in thread "CompileThread0" java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate

Possible causes:

         - not enough swap space left, or

         - kernel parameter MAXDSIZ is very small.

-          java.lang.OutOfMemoryError: unable to create new native thread

 

위 두 에러가 나는 이유는 Data 사이즈가 Full 차서 더 이상 메모리 할당을 할 수 없다는 java.lang.OutOfMemoryError 입니다.

 

Jdk도 내부적으로 c 라이브러리를 쓰고 jni를 통해서 c프로그램을 호출할 수 도 있는 환경에서 Data 영역이상으로 메모리 사용 시 위 에러를 만날 수 있습니다.

Heap 영역에는 java heap과 C heap이 있는데 C heap이 Data 메모리 영역에 영향을 미치는 것으로 보이며 보통 C의 전역 변수들이 잡히는 영역입니다.

 

위 현상을 만났을 때는 hp os의 MAXDSIZ가 너무 작게 잡혀있지 않은지 확인 후 적당한 크기로 늘려 줘야 합니다.

 

Glance 에서 shift+m 을 누른 후 jvm pid를 누르면 java가 사용하는 Data 사이즈를 모니터링 할 수 있습니다.

모니터링을 통해 적정한 사이즈로 MAXDSIZ를 늘려주어야 하며 만일 늘려 준게 에러 발생의 시간만 지연 시킬 뿐 계속 사용량이 늘어난다면 이는 사용하는 c라이브러리 쪽에 메모리 릭 버그가 있는 것이므로 c라이브러리를 체크 하셔야 합니다.

java.lang.OutOfMemoryError: unable to create new native thread

이 경우는 프로그램에서 Thread pool 프로그램 실수로 필요이상으로 쓰레드가 생성되는 경우도 과도하게 메모리를 사용할 수 있으므로 jvm 쓰레드 덤프를 통해 과도한 쓰레드가 생성되지 않았는지도 확인해 보셔야 합니다.

 

2.     Heap이 Full 찾을 때

이 경우는 java가 사용하는 heap 영역이 Full 되서 java.lang.OutOfMemoryError 가 나는 경우 인데 두 가지 패턴이 있습니다.

순간적으로 대량의 데이터를 메모리에 올리는 프로그램이 실행되어 문제를 야기 할수 있으며 다른 한 가지는 조금씩 메모리 릭이 발생하여 점차적으로 메모리가 쌓여 가는 경우 입니다.

두 가지 중에  어느 것인지 구별하는 것이 중요하며 이를 위해서도 마찬가지로 -verbosegc 또는  -XX:+PrintGCDetails  로그를 통해 순간적으로 메모리가 차는 것인지 조금씩 메모리가 차는 것인지를 확인하셔야 합니다.

 

1) 특정 프로그램의 특정시점의 과도한 메모리 사용에 의한 경우

                특정 프로그램이 과도하게 heap 메모리 이상 메모리를 사용하면서

java.lang.OutOfMemoryError가 발생하는 경우는 이 에러가 발생하는 시점의 쓰레드 덤프를 통해 어느 프로그램인지 쉽게 찾을 수 있습니다.

쓰레드 덤프에 메모리를 쓸만한 프로그램은 어느 정도의 자바프로그램 경험이 있으면 찾을 수 있습니다.

 

Ex)

"ExecuteThread: '36' for queue: 'default'" daemon prio=10 tid=0x0048e7b0 nid=48 lwp_id=4139729 runnable [0x23f32000..0x23f30500]

          at java.net.SocketInputStream.socketRead(Native Method)

          at java.net.SocketInputStream.read(Unknown Source)

          at oracle.net.ns.Packet.receive(Unknown Source)

          at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)

          at oracle.net.ns.NetInputStream.read(Unknown Source)

          at oracle.net.ns.NetInputStream.read(Unknown Source)

          at oracle.net.ns.NetInputStream.read(Unknown Source)

          at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:718)

          at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:690)

          at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:373)

          at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1405)

          at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:889)

          - locked <0x35e8e3b0> (a oracle.jdbc.ttc7.TTC7Protocol)

          at oracle.jdbc.driver.OracleResultSetImpl.next(OracleResultSetImpl.java:242)

          - locked <0x36f66c98> (a oracle.jdbc.driver.OracleResultSetImpl)

          at weblogic.jdbc.pool.ResultSet.next(ResultSet.java:180)

          at Dcard.AAA.ejb.monitor.member.wbbb.WACBean.getInfoList(WACBean.java:5789)

 

java.lang.OutOfMemoryError 가 날 상황에 쓰레드 덤프를 두 세번 떠서 같은 쓰레드 번호로 위 같은 로직이 오래 결려있다면 이는 프로그램에서 while(rs.next()) 를 오랜 기간 돌면서 메모리에 DB로부터 읽어서 올리고 있다는 것을 의미 합니다 대량 데이터 조회가 일어나는 경우인 것 입니다.

 

이런 식으로 특정 프로그램의 특정 시점에 과도한 메모리 사용으로 인한 java.lang.OutOfMemoryError 에러는 쓰레드 덤프를 통해 찾을 수 있습니다.

-- 물론 2번에서 설명 할 heap dump를 통해서도 찾을 수 있습니다.

 

2) 메모리 릭에 의해 조금씩 메모리가 쌓인 경우

JVM위 에서 실행중인 프로그램중에 어떤 것이 GC 대상에서 제외되는 영역에 data를 조금씩 쌓고 있다는 얘기입니다.

GC 대상에서 제외되는 영역은 다음과 같은 곳이 있습니다.

- Http Session에 넣는 데이터..(세션 타임아웃 또는 invilidate 씨 까지 제외됨)

- Static 변수

- 서블릿 또는 jsp의 멤버변수 ( WAS 기동 후 최초 호출 시 인스턴스 화 되어 WAS가 내려 갈 때 까지 사라지지 않음 )

- EJB의 멤버변수( pool안에서 객체가 존재하는 한 GC대상에서 제외)

          위 같은 영역에 프로그램에서 data를 add 하는 구조를 메모리 릭 이라고 할 수 있습니다.

           IBM 또는 SUN JDK 인 경우에는 heapdump를 통해서 분석하여 어느 데이터가 메모리를 많이 잡고 있는지를 알 수 있습니다.

           Heapdump 사용법 및 분석 법은 다음을 참조 하시면 됩니다.

 http://www.alphaworks.ibm.com/aw.nsf/FAQs/heaproots

http://www-1.ibm.com/support/docview.wss?uid=swg21190476

http://www-1.ibm.com/support/docview.wss?rs=180&context=SSEQTP&q1=heapdump+solaris&uid=swg21190608&loc=en_US&cs=utf-8&lang=en

http://www.skywayradio.com/tech/WAS51/IBMHeapDump/

 

Posted by marryjane
|

http://okjsp.pe.kr/seq/122183

jxl은 대용량데이터를 엑셀문서를 통해 입력할때 속도가 빠릅니다.
단점은 엑셀형식으로 데이터를 받았을때 제대로 된 엑셀이 아니라 조작이 좀 그렇습니다.
반대로 poi는 데이터를 엑셀문서로 다운받았을때 제대로 된 엑셀문서라 현업들이 바로 조작하고 사용하기가 편합니다.
하지만 데이터를 넣을때 소량은 상관없지만 수만개이상넣을때..좀 느립니다.
그래서 저도 지금까지 4,5번해봤지만 데이터를 넣을때는 jxl로 데이터를 받을때는 poi로 합니다.
Posted by marryjane
|

http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10102&eid=X3/YQCPY9/+a9yR4FpqEJlYn4mNSb0LE&qb=anhsIMDMuczB9g==&pid=fK8I1soi5Uhssb7go4ssss--154326&sid=SM85U5giz0gAAFizEvo

테스트는 안해봤음.

import java.awt.Graphics;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import jxl.*;
import java.io.*;
import java.sql.*;

public class Test3 {
    //frame의 내부무명클래스의 메서드에서 사용할수 있게 전역변수로 설정함
    javax.swing.ImageIcon icon = null;    


    public void excel2(){
        try{
            Workbook workbook = null;
            Sheet sheet = null;
           
            workbook = Workbook.getWorkbook(new File("excel/test.xls"));//엑셀파일 위치입니다.
           
            sheet = workbook.getSheet(0);
           
            //이미지 갯수를 카운트합니다
            for(int i=0;i<sheet.getNumberOfImages();i++) {
                //가지고 오는 이미지의 사이즈 체크
                if(sheet.getDrawing(i).getImageData().length>0) {
                    //이미지 파일을 생성하고 for문 종료합니다.
                    icon = new javax.swing.ImageIcon(sheet.getDrawing(i).getImageData());
                    break;
                }
            }
           
            javax.swing.JFrame frame = new javax.swing.JFrame(){
                //paint메소드 상속하여 구현해줌....icon은 Test3 클래스의 icon입니다.
                public void paint(java.awt.Graphics g) {
                    g.drawImage(icon.getImage(), 0, 0, this);
                }

            };
           
            frame.setSize(500, 500);
           
            frame.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
           
            frame.setVisible(true);


        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Test3().excel2();
    }
}

Posted by marryjane
|