回答:
あなたの例では、私の推測がstr
されていないの外使用while
の内側にそれを宣言しているため、それ以外の場合は、あなたが質問をされない、ループをwhile
ループするオプションではありません、それはコンパイルされないであろうから、。
したがって、以降はstr
されないための最小の可能な範囲は、ループ外で使用str
され内 whileループ。
したがって、答えは、whileループ内で絶対的に宣言する必要があるということを強調しstr
ています。ifs、nos、notはありません。
このルールに違反する唯一のケースは、何らかの理由ですべてのクロックサイクルをコードから絞り出す必要がある場合に非常に重要です。その場合、外部スコープでインスタンス化し、それを再利用することを検討します。内部スコープのすべての反復でそれを再インスタンス化します。ただし、これはJavaの文字列が不変であるため、この例には当てはまりません。strの新しいインスタンスは常にループの最初に作成され、ループの最後で破棄する必要があるため、そこで最適化する可能性はありません。
編集:(私のコメントを以下の回答に挿入します)
いずれにせよ、適切な方法は、すべてのコードを適切に記述し、製品のパフォーマンス要件を確立し、この要件に対して最終製品を測定し、それを満たさない場合は、最適化することです。そして、通常、最終的に発生するのは、コードベース全体を調べて調整し、ハックする代わりに、プログラムがパフォーマンス要件を満たすようにするためのいくつかの場所で、いくつかの優れた正式なアルゴリズム最適化を提供する方法を見つけることです。あちこちでクロックサイクルを圧迫するために。
これら2つの(類似した)例のバイトコードを比較しました。
1.例を見てみましょう。
package inside;
public class Test {
public static void main(String[] args) {
while(true){
String str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
後はjavac Test.java
、javap -c Test
次のようになります。
public class inside.Test extends java.lang.Object{
public inside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
2.例を見てみましょう。
package outside;
public class Test {
public static void main(String[] args) {
String str;
while(true){
str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
後はjavac Test.java
、javap -c Test
次のようになります。
public class outside.Test extends java.lang.Object{
public outside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
観察結果は、これら2つの例の間に違いがないことを示しています。それはJVM仕様の結果です...
ただし、コーディングのベストプラクティスの名の下で、可能な限り小さいスコープで変数を宣言することをお勧めします(この例では、変数が使用される唯一の場所であるため、ループ内にあります)。
final
愛好家にとって:パッケージの場合とstr
同じようfinal
に宣言しても違いはありません=)inside
最小スコープでオブジェクトを宣言すると、読みやすさが向上します。
今日のコンパイラではパフォーマンスは問題になりません。(このシナリオでは)
保守の観点からは、2番目のオプションの方が優れています。
可能な限り狭い範囲で、同じ場所で変数を宣言して初期化します。
以下のようドナルド・アーヴィン・クヌースは言いました:
「私たちは小さな効率については忘れる必要があります。たとえば、約97%の時間を言います。時期尚早の最適化がすべての悪の根源です」
つまり、プログラマーがパフォーマンスを考慮してコードの設計に影響を与える状況。これにより、コードが最適化によって複雑になり、プログラマーが最適化によって気が散らされるため、デザインがきれいになっていない場合や、コードが正しくない場合があります。
更新された回答にスキップしてください...
パフォーマンスを重視する場合は、System.outを取り出し、ループを1バイトに制限します。double(テスト1/2)とString(3/4)を使用した経過時間(ミリ秒)は、Windows 7 Professional 64ビットとJDK-1.7.0_21で以下に示されています。バイトコード(test1とtest2についても以下に示す)は同じではありません。変更可能で比較的複雑なオブジェクトをテストするのが面倒でした。
ダブル
Test1の所要時間:2710ミリ秒
Test2の所要時間:2790ミリ秒
文字列(テストではdoubleを文字列に置き換えるだけです)
Test3の所要時間:1200ミリ秒
Test4の所要時間:3000ミリ秒
バイトコードのコンパイルと取得
javac.exe LocalTest1.java
javap.exe -c LocalTest1 > LocalTest1.bc
public class LocalTest1 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
double test;
for (double i = 0; i < 1000000000; i++) {
test = i;
}
long finish = System.currentTimeMillis();
System.out.println("Test1 Took: " + (finish - start) + " msecs");
}
}
public class LocalTest2 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for (double i = 0; i < 1000000000; i++) {
double test = i;
}
long finish = System.currentTimeMillis();
System.out.println("Test1 Took: " + (finish - start) + " msecs");
}
}
Compiled from "LocalTest1.java"
public class LocalTest1 {
public LocalTest1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: dconst_0
5: dstore 5
7: dload 5
9: ldc2_w #3 // double 1.0E9d
12: dcmpg
13: ifge 28
16: dload 5
18: dstore_3
19: dload 5
21: dconst_1
22: dadd
23: dstore 5
25: goto 7
28: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
31: lstore 5
33: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
36: new #6 // class java/lang/StringBuilder
39: dup
40: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
43: ldc #8 // String Test1 Took:
45: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
48: lload 5
50: lload_1
51: lsub
52: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
55: ldc #11 // String msecs
57: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
63: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: return
}
Compiled from "LocalTest2.java"
public class LocalTest2 {
public LocalTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: dconst_0
5: dstore_3
6: dload_3
7: ldc2_w #3 // double 1.0E9d
10: dcmpg
11: ifge 24
14: dload_3
15: dstore 5
17: dload_3
18: dconst_1
19: dadd
20: dstore_3
21: goto 6
24: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
27: lstore_3
28: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
31: new #6 // class java/lang/StringBuilder
34: dup
35: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
38: ldc #8 // String Test1 Took:
40: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: lload_3
44: lload_1
45: lsub
46: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
49: ldc #11 // String msecs
51: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
54: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
57: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
}
パフォーマンスをすべてのJVM最適化と比較することは本当に簡単ではありません。ただし、多少は可能です。Google Caliperでのより良いテストと詳細な結果
これは上記のコードと同じではありません。ダミーループをコーディングするだけの場合、JVMはそれをスキップするので、少なくとも何かを割り当てて返す必要があります。これはCaliperのドキュメントでも推奨されています。
@Param int size; // Set automatically by framework, provided in the Main
/**
* Variable is declared inside the loop.
*
* @param reps
* @return
*/
public double timeDeclaredInside(int reps) {
/* Dummy variable needed to workaround smart JVM */
double dummy = 0;
/* Test loop */
for (double i = 0; i <= size; i++) {
/* Declaration and assignment */
double test = i;
/* Dummy assignment to fake JVM */
if(i == size) {
dummy = test;
}
}
return dummy;
}
/**
* Variable is declared before the loop.
*
* @param reps
* @return
*/
public double timeDeclaredBefore(int reps) {
/* Dummy variable needed to workaround smart JVM */
double dummy = 0;
/* Actual test variable */
double test = 0;
/* Test loop */
for (double i = 0; i <= size; i++) {
/* Assignment */
test = i;
/* Not actually needed here, but we need consistent performance results */
if(i == size) {
dummy = test;
}
}
return dummy;
}
要約:declaredBeforeは、パフォーマンスが非常に小さいことを示しています。これは、スコープの最小原理に反しています。JVMが実際にこれを行うはずです
str
afterループ(スコープ関連)を使用する必要がない場合は、2番目の条件、つまり
while(condition){
String str = calculateStr();
.....
}
がcondition
trueの場合にのみスタック上にオブジェクトを定義すると、つまり、必要に応じて使用します
あなたの質問に答えるのに最適なリソースは次の投稿だと思います:
私の理解によると、これは言語に依存するでしょう。IIRC Javaはこれを最適化するため、違いはありませんが、JavaScript(たとえば)はループ内で毎回メモリ全体の割り当てを行います。特にJavaでは、プロファイリングを実行すると、2番目がより速く実行されると思います。
多くの人が指摘しているように、
String str;
while(condition){
str = calculateStr();
.....
}
これよりは良くありません:
while(condition){
String str = calculateStr();
.....
}
したがって、再利用しない場合は、スコープ外の変数を宣言しないでください...
変数は、使用される場所にできるだけ近い場所で宣言する必要があります。
RAII (Resource Acquisition Is Initialization)が簡単になります。
それは変数のスコープをタイトに保ちます。これにより、オプティマイザの動作が向上します。
Google Android開発ガイドによると、変数のスコープは制限する必要があります。このリンクを確認してください:
str
変数が利用可能であるとさえコードの下に実行しながら、後にメモリ内の一部の空間を予約します。
String str;
while(condition){
str = calculateStr();
.....
}
str
変数が使用できなくなり、またメモリのために割り当てられたリリースされるstr
コードの下に変数。
while(condition){
String str = calculateStr();
.....
}
2番目の方法を確実に実行すると、システムメモリが減少し、パフォーマンスが向上します。
確かに、上記の質問はプログラミングの問題です。コードをどのようにプログラムしますか?どこに「STR」にアクセスする必要がありますか?ローカルでグローバル変数として使用される変数を宣言する必要はありません。私は信じているプログラミングの基礎。
この質問のほとんどすべての人への警告:Java 7を使用しているコンピューターでは、ループ内で200倍の速度になりやすいサンプルコードを次に示します(メモリの消費量も少し異なります)。しかし、それは割り当てに関するものであり、スコープだけではありません。
public class Test
{
private final static int STUFF_SIZE = 512;
private final static long LOOP = 10000000l;
private static class Foo
{
private long[] bigStuff = new long[STUFF_SIZE];
public Foo(long value)
{
setValue(value);
}
public void setValue(long value)
{
// Putting value in a random place.
bigStuff[(int) (value % STUFF_SIZE)] = value;
}
public long getValue()
{
// Retrieving whatever value.
return bigStuff[STUFF_SIZE / 2];
}
}
public static long test1()
{
long total = 0;
for (long i = 0; i < LOOP; i++)
{
Foo foo = new Foo(i);
total += foo.getValue();
}
return total;
}
public static long test2()
{
long total = 0;
Foo foo = new Foo(0);
for (long i = 0; i < LOOP; i++)
{
foo.setValue(i);
total += foo.getValue();
}
return total;
}
public static void main(String[] args)
{
long start;
start = System.currentTimeMillis();
test1();
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
test2();
System.out.println(System.currentTimeMillis() - start);
}
}
結論:ローカル変数のサイズによっては、それほど大きな変数でなくても、その差は非常に大きくなる可能性があります。
ループの外側または内側が問題になることもあります。
bigStuff[(int) (value % STUFF_SIZE)] = value;
(2147483649Lの値を試してください)
あなたはリスク持っているNullPointerException
あなたの場合はcalculateStr()
メソッドの戻りはヌルをし、その後、あなたはstrの上のメソッドを呼び出すようにしてください。
より一般的には、null値の変数を持つことは避けてください。ちなみに、それはクラス属性に対してより強力です。
NullPointerException.
このコードを実行しようreturn str;
とすると、コンパイルエラーが発生するリスクはありません。