読者です 読者をやめる 読者になる 読者になる

ProMemoGram

プログラミング関連のメモ。

【Vagrantfileの設定】Vagrantの共有フォルダがホスト側から仮想環境への一方向にしか同期してくれない時の解決法

おそらく同じような問題で悩んでる人はいないと思いますが、万が一僕みたいなバカが他にもいた時のために一応記事にしておきます。

症状:Vagrantの共有フォルダがホスト側から仮想環境への一方向にしか同期しない

Vagrantfileは正しく記述されているのに、なぜか共有フォルダがうまく機能しない。
仮想環境上の共有フォルダにファイルを置いても、ホスト側の共有フォルダにはファイルが同期されないが、逆の場合はちゃんと同期されてる。

解決法:typeオプションでtype: "virtualbox"を指定する

typeで指定できるのは、共有フォルダのタイプです。
種類は3タイプで、virtualboxNFSrsync
これを指定していないと、デフォルトでNFSが選ばれることになる。
しかし、WindowsNFSが使えないため、rsyncが適応されることになる。これこそが問題の原因。
rsyncは一方向への同期しかサポートしていない。
よって、明示的にtypeをvirtualboxに指定してやればこの問題は解決する。

config.vm.synced_folder ".", "/vagrant", type: "virtualbox"

具体的にはこんな感じ

【Java】値渡しの参照渡しの違い

メソッド引数の引き渡しにおける、値渡しと参照渡しの違いを記述しておこうと思います。

値渡しと参照渡し

値渡し

メソッド引数の引き渡しの際、基本データ型の場合は値が直接コピーされます。
基本データ型とは、以下のデータ型のことです。

表1:基本データ型

説明
char 16ビットUnicodeキャラクタデータ
boolean 真偽値
byte 8ビット符号付き整数
short 16ビット符号付き整数
int 32ビット符号付き整数
long 64ビット符号付き整数
float 32ビット符号付き浮動小数点数
double 64ビット符号付き浮動小数点数

値がコピーされて使用されるだけなので、そのメソッド内でいくら値を変更しようとメソッドの外に影響することはありません。以下に例を示します。

public class Test {
	public static void main(String[] args) {
		int v = 10;

		System.out.println(v); //変数vの値を表示
		x(v);          //x()メソッドの実行
		System.out.println(v); //変数vの値を表示
	}

	static void x(int v) {
		v = 20;
	}
}


実行結果

10
10

このプログラムの実行結果を見れば分かる通り、いくらx()メソッド内でvの値を変更しようと、main()メソッドのvの値は変化しません。

このプログラムで起こっていることを簡単に説明したいと思います。

まず、main()メソッドの初めで、変数vを用意し、値を代入しています。

この時、プログラムはコンピュータのメモリ上の適当な場所に、4バイトのメモリ領域を確保します(整数型は32ビット(=4バイト)であるため)。

そしてその確保した領域に、値を格納します。イメージとしては以下の表2のような感じです。

表2:main()メソッドの変数vが指すメモリ番地と値

メモリ番地
2000-2003 10

この時点でmain()メソッドの変数vの値を表示すれば、当然表示結果は10となります。これが実行結果の1行目です。

次にこのmain()メソッドの変数vの値をx()メソッドに渡し、x()メソッドを実行しています。

x()メソッドは渡されたvの値を引数vにコピーするわけですが、この時、プログラムは再びメモリ上の適当な場所に4バイトのメモリ領域を確保します。

そして、確保した領域に、main()メソッドから渡された値を格納します。これが「値をコピーする」ということです。
具体的には以下のようなイメージです。

表3:x()メソッドの引数vが指すメモリ番地と値

メモリ番地
5000-5003 10

変数名は同じvですが、メモリの番地が違います。x()メソッドが指すvは、メモリ番地5000-5003なので
いくらここの値を書き換えたところで、メモリ番地2000-2003(main()メソッドが指すv)に格納されている値には何の影響もないわけです。

なので、x()メソッド内でxの値を20に書き換えたところで、main()メソッドの変数vの値は変わることはありません。

当然、x()メソッドを実行後にmain()メソッドで変数vの値を表示しても、値は10のままです。その表示結果が実行結果の2行目です。

参照渡し

上記で説明した値渡しは、基本データ型の場合のみ使用されます。

配列、オブジェクトを引数として渡す場合には参照渡しといって、参照だけをメソッドに渡すことになります。

つまり、メソッド内で値を書き換えると、メソッドの外にも影響を及ぼします。

以下の具体例を示します。

public class Test {
	public static void main(String[] args) {
		int v[] = {0,1,2,3,4};

		display(v);  //vの値を表示
		x(v);        //x()メソッドの実行
		display(v);  //vの値を表示
	}

	//配列の中身を書き換える
	static void x(int v[]) {
		v[0] = 9;
	}

	//配列の中身を表示するだけの関数
	static void display(int v[]) {
		for(int i = 0; i < v.length; i++) {
			System.out.print(v[i]);
		}
		System.out.println("");
	}
}


実行結果

01234
91234

実行結果を見ればわかるように、x()メソッドを実行する前と後では、main()メソッドにおける配列変数v(以下v)の値が書き換わってしまっています。

これはmain()メソッドがvの値ではなく参照をx()メソッドに渡していることが原因です。

まず、このプログラムではmain()メソッド内で要素数5のvを用意し、値を格納しています。

この時、プログラムはコンピュータのメモリ上の適当な場所にvの領域を確保し、そこに値を格納します。

具体的には以下のようなイメージです。

表4:main()メソッドのv[]が指すメモリ番地と値

メモリ番地
2000-2003 0
2004-2007 1
2008-2011 2
2012-2015 3
2016-2024 4

v(=v[0])にはこの先頭番地が格納されます。つまり2000という値(参照)が格納されているわけです。

先頭の番地さえ分かっていれば、v[1]は先頭番地から4バイト先、v[2]は先頭番地から8バイト先、と計算できるからです(整数型は4バイトであるため)。

この時点でvの値を表示すれば、当然表示結果は01234となります。それが実行結果の1行目です。

次にこのvをx()メソッドに渡し、x()メソッドを実行していますが、この時、基本データ型とは違い参照を渡しています。

参照を渡すというのはどういうことかというと、vの値、つまり先ほど確保したメモリ領域の先頭番地がx()メソッドの引数vに渡されるわけです。

つまり、main()メソッドのvもx()メソッドのvもmain()メソッドで確保した領域の先頭番地である「2000」が格納されている状態になります。

そのため、x()メソッド内でvの値を変更すると、当然main()メソッドのvにも影響が出ます。main()メソッドでのv[0]とx()メソッドでのv[0]は全く同じ場所を指しているわけですからね。

ちなみに何故値を直接渡さないのか?という疑問ですが、それは単にメモリの無駄だからです。

値渡しの項目で説明した通り、値渡しではメソッドに値がコピーされて使用されます。

値をコピーするということは、その値をコピーするためのメモリ領域を余分に確保する必要があります。

例えばint型の変数をコピーして使用する場合、メソッドの呼び出し元で4バイトのメモリを確保し
さらに呼び出されたメソッドがコピー用にもう4バイト確保する必要があるので、計8バイトの領域を確保する必要があります。

基本データ型の場合はこれでも構わないでしょう。なぜなら、表1を見ればわかる通り、基本データ型は多くても64ビット(=8バイト)しかメモリ領域を必要としないため
コピーするための領域をもう一つ確保したところでたかがしれてるわけです。

しかし、配列なら話は変わってきます。例えばint型の要素数1000の配列を使用する場合を考えると、確保するべきメモリ領域は4×1000で4000バイトも必要になります。

これをコピーしようとすると、もう一つ余分に4000バイトのメモリ領域を確保することになり、合計で8000バイトもの領域を確保することになります。

これはとても無駄ですよね。特にメソッド内で値を参照するだけなら、尚更無駄です。

よって、呼び出し元のメソッドで既に確保した領域の参照(配列の先頭番地)を渡すことで、余分にメモリ領域を確保する必要をなくしているわけです。

例では配列変数で説明しましたが、オブジェクト変数でもほとんど同じです。要は値をそのままコピーすると無駄が多いので、参照(先頭番地)を渡すことになっているというだけです。

まとめ

いろいろごちゃごちゃと説明しましたが、結局値渡しの参照渡しの違いは、メソッドの呼び出しによって、呼び出し元の変数に影響があるか否かである、という程度の認識でいいと思います。

c++なんかでは明示的に参照渡しであることを記述する必要がありましたが、javaでは勝手にやってくれるので、別にプログラマが意識する必要はないからです。