3

私たちのプロジェクトの 1 つで、ユーザーは自分のアカウントにファイルを添付できます。これらのファイルは MS-SQL データベースに保存されます。したがって、次のコードがあります。

@Entity
public class File extends AbstractEntity {

    @Lob
    @Basic
    private byte[] data;

    @Nullable
    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public File() {
    }

    public File(byte[] data) {
        this.data = data;
    }
}

public class SomeBean {

    @PersistenceContext
    protected EntityManager em;

    public Long uploadFile(@NotNull byte[] data) {
        final PhysicalFile physicalFile = new PhysicalFile();
        physicalFile.setData(data);
        em.persist(physicalFile);
        return physicalFile.getId();
    }
}

そして、40 MB のファイルをアップロードしようとする前に、すべてが素晴らしくきれいで、メソッド内java.lang.RuntimeException: javax.transaction.RollbackException: [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] Can't commit because the transaction is in aborted stateで発生した を取得しました。java.lang.OutOfMemoryError: Java heap spaceuploadFile()

ヒープダンプを作成し、VisualVM で確認しました。
ヒープダンプ

の 400+ MBchar[]および 100+ MB のbyte[]. を含む私たちのアプリケーションは、開始時JBossに約 60 ~ 65 MB のヒープを使用していました。では、問題は、なぜEntityManagerヒープ メモリを狂ったように消費するのかということです。

4

1 に答える 1

2

私はあなたの問題を次のように理解しています。

  • EntityManagerを介してロード/永続化するすべてのエンティティは、エンティティを明示的にデタッチするまで(EntityManager.detach()またはEntityManager.clear()またはEntityManager.close()を介して)メモリに残ります。したがって、短命のEntityManagerを使用することをお勧めします。

  • ビジネスロジックでRuntimeExceptionが発生する限り、emEntityManagerは開いたままになります。この種のコードは常に避けたいと思うでしょう。EntityManagerの作成と終了は次のように考えることができます。

    public Customer getBestCustomerOfMonth() {
    EntityManagerFactory emf = ... ;
    EntityManager em = emf.createEntityManager();
    // business logic
    em.close();
    }
    
  • EntityManager em.close();を閉じるための行をネストできます。ファイナルブロック内

  • EntityMangersの場合と同じ方法でトランザクションを閉じる(コミットまたはロールバックする)必要があるため、エンタープライズアプリケーションサーバーの外部でトランザクションを使用する場合。これらのリソース(EntityManagerと基になるトランザクションの両方)を閉じるには、追加レベルのネストを作成し、次のようなコードを記述する必要があります。

         public Customer updateCustomer(Customer cust) {
    
           EntityManagerFactory emf = ... ;   EntityManager em =
         emf.createEntityManager();   try {
         EntityTransaction t = em.getTransaction();
         try {
           t.begin();  
           // business logic to update the customer
           em.merge(cust);
           t.commit();
         } finally {
           if (t.isActive()) t.rollback();
         }   } finally {
         em.close();
           }       
        }
    

このネストされた構造は少し混乱しているように見えるかもしれませんが、トランザクションの前に本当に必要です。

于 2012-12-06T08:37:47.957 に答える