Transientキーワードでビューステート軽減

スポンサーリンク

パフォーマンスも上級のテスト範囲

認定上級platformデベロッパーのテストともなると、パフォーマンスを考慮したプログラミングについても問われます。
ビューステートは135KBと定められており、これを越えるとビューステートエラーが出ます。これは経験した人も結構多いんじゃないでしょうか?
試験でパフォーマンスで問われるのは以下の2点かと思います

  • いかにビューステートを抑えるか
  • いかに画面表示を早く見せるか

その中での対策方法として、Transientキーワードを使う、ページングを利用する、検索部分を切り離して非同期で実行するなどの対策があります。

ビューステートエラーはプログラマーの天敵

これが例のビューステートエラー。
本番ででたら怒られるヤツですね。。。
Apexコントローラーの天敵ビューステートエラー

ビューステートエラーは例外で拾えないし、お金を出して上限を増やせるわけではないので、
プログラマーは何としてもビューステートを抑えるように制限しないといけません。

ここではまず、ビューステートの軽減の方法としてTransientキーワード利用を取り上げたいと思います。
他の対策については、またどこかでやると思います。

ビューステート解消の切り札

そこで登場するのがTransientキーワード。
単語の意味がわからなかたので、一応調べてみると、「一時の」「瞬間的な」「つかの間の」「はかない」「無常の」という意味らしいです。段々ネガティブになっていますが、ApexでのTransientはそこまでマイナスの意味を持たないのでご安心を。
これを使うことで、ビューステートがかなり抑えられます。

Transientの使い方

使い方は、Apex内でいつもやっている変数宣言を

Integer i;

このようにTransientキーワードつけるだけ

Transient Integer i;

これでビューステートが抑えられるのだから驚きだ。
Transientのあり・なしでどれだけ変わるか実験してみました。

実際に試してみる

Transientを使わない方法

まずはTransientなしのVisualForceとApexコントローラを作成
ちなみに、ここで使われるオブジェクトTransientData__cはカスタム項目のないカスタムオブジェクトです。

TransientPageWithoutTransient.page

<apex:page controller="TransientControllerWithoutTransient" action="{!init}">
    <apex:form >
        <apex:repeat value="{!tList}" var="td">
            <apex:outputText value="{!td.Name}"/>
        </apex:repeat>
    </apex:form>
</apex:page>

TransientControllerWithoutTransient.cls

public class TransientControllerWithoutTransient {
    // Transientありでリストの変数宣言
    Transient public List tList{get;set;}
    
    public void init(){
        tList = [Select Id, Name From TransientData__c];
    }
}

Transientを使う方法

TransientPageWithTransient.page

<apex:page controller="TransientControllerWithTransient" action="{!init}">
    <apex:form >
        <apex:repeat value="{!tList}" var="td">
            <apex:outputText value="{!td.Name}"/>
        </apex:repeat>
    </apex:form>
</apex:page>

TransientControllerWithTransient.cls

public class TransientControllerWithTransient {
    // Transientありでリストの変数宣言
    Transient public List tList{get;set;}
    
    public void init(){
        tList = [Select Id, Name From TransientData__c];
    }
}

比較結果は!?

対象はいずれも1000レコードでそれぞれ画面を表示させてみました。
結果はこの通り!

ちょっとVisualForce名が若干違いますが気にしないでください。。。

Transient有無のメモリ量比較

Transientなし
9.06KBTransientあり
1.75KB

なんとその差は約7.3KB。80%もメモリが削減したことに
「一時的」だけあって、VisualForce表示後はメモリから消すのかな(コンソールにも表示されなかったし)。
正直、僕もここまで劇的に変わるとは思いませんでした。

>追記
Apexクラスにある変数やリストなどを、DBやブラウザに送信するように加工することをシリアライズ化と言うのですが、
Transientキーワードはこのシリアライズ化を行わないと言う宣言になります。
このシリアライズ化しないと言うこともキーワードとして覚えておいてください。

注意点

このように、ビューステート解消の第一手段となるTransientだが、一個注意点があります。

インライン編集など画面の入力を伴い、サーバへ入力内容を送るような使い方をする場合はTransientは使えないので注意。
上記の例だとVisualForceのところの<apex:outputText>を<apex:inputText>をにすると、NGってことになります。
ReadOnlyのようなイメージに近いでしょう。

どうしても、インライン編集などで<apex:inputText>や<apex:inputField>を使いたい場合は、リストの件数を泣く泣く減らすか、ページングを使ってうまく乗り切るしかないです。
ページングを使った場合でも、入力内容を保持したりでなかなか難しいので、機会があればまたやろうと思います。

最後にまとめ

  • Transient変数でビューステート大幅削減
  • <apex:inputText>などの入力を伴うものはTransient変数は使えない

コメント