제네릭을 사용한 박싱 및 언 박싱
정수 컬렉션을 만드는 .NET 1.0 방법 (예 :)은 다음과 같습니다.
ArrayList list = new ArrayList();
list.Add(i); /* boxing */
int j = (int)list[0]; /* unboxing */
이것을 사용하는 불이익은 권투 및 개봉으로 인한 유형 안전성 및 성능 부족입니다.
.NET 2.0 방법은 제네릭을 사용하는 것입니다.
List<int> list = new List<int>();
list.Add(i);
int j = list[0];
boxing의 가격은 (내 이해에) 힙에 객체를 만들고 스택 할당 정수를 새 객체에 복사하고 그 반대의 경우 unboxing을 수행해야한다는 것입니다.
제네릭의 사용은 이것을 어떻게 극복합니까? 스택 할당 된 정수가 스택에 남아 있고 힙에서 가리키고 있습니까 (범위를 벗어날 때 어떤 일이 발생하기 때문에 이것이 사실이 아닌 것 같습니다)? 여전히 스택의 다른 곳에 복사해야 할 필요가있는 것 같습니다.
정말 무슨 일이 일어나고 있습니까?
컬렉션과 관련하여 제네릭은 T[]
내부적으로 실제 배열 을 활용하여 boxing / unboxing을 피할 수 있습니다 . List<T>
예를 들어 T[]
배열을 사용하여 내용을 저장합니다.
배열은 물론, 참조 형 및 힙에 저장합니다 (CLR, 궁시렁 궁시렁의 현재 버전에서), 따라서이다. 그러나 a T[]
가 아니고 a 가 아니기 object[]
때문에 배열의 요소는 "직접"저장 될 수 있습니다. 즉, 여전히 힙에 있지만 박스형이 아니고 배열에에 대한 참조를 포함하는 대신 배열 의 힙 에 있습니다. 상자들.
그래서 위해 List<int>
, 예를 들어, 당신은 무엇을이 같은 배열 것 "모양"에있는 것입니다 :
[1 2 3]
이것을를 ArrayList
사용하고 object[]
따라서 다음과 같이 "보이게" 하는 과 비교하십시오 .
[* a * b * c]
... 여기서 *a
등은 객체에 대한 참조입니다 (박스형 정수) :
* a-> 1 * b-> 2 * c-> 3
그 조잡한 예를 용서하십시오. 내가 의미하는 바를 알기를 바랍니다.
혼란은 스택, 힙 및 변수 간의 관계를 오해 한 결과입니다. 그것에 대해 생각하는 올바른 방법이 있습니다.
- 변수는 유형이있는 저장 위치입니다.
- 변수의 수명은 짧거나 길 수 있습니다. "짧다"는 "현재 함수가 반환하거나 던질 때까지"를 의미하고 "long"은 "이보다 더 길다"를 의미합니다.
- 변수 유형이 참조 유형 인 경우 변수의 내용은 수명이 긴 저장 위치에 대한 참조입니다. 변수 유형이 값 유형이면 변수의 내용은 값입니다.
구현 세부 사항으로 수명이 짧은 저장 위치를 스택에 할당 할 수 있습니다. 오래 지속될 수있는 스토리지 위치가 힙에 할당됩니다. "값 유형은 항상 스택에 할당됩니다." 값 유형이 항상 스택에 할당 되는 것은 아닙니다.
int[] x = new int[10];
x[1] = 123;
x[1]
저장 위치입니다. 수명이 길다. 이 방법보다 오래 살 수 있습니다. 따라서 힙에 있어야합니다. int를 포함한다는 사실은 관련이 없습니다.
boxed int가 비싼 이유를 올바르게 말하십시오.
boxing의 가격은 힙에 객체를 생성하고, 스택 할당 정수를 새 객체에 복사하고, 그 반대로 unboxing을 수행해야한다는 것입니다.
잘못된 부분은 "스택 할당 정수"라고 말하는 것입니다. 정수가 할당 된 위치는 중요하지 않습니다. 중요한 것은 저장소 에 힙 위치에 대한 참조를 포함 하는 대신 정수 가 포함 되어 있다는 것 입니다. 가격은 개체를 만들고 복사하는 데 필요한 것입니다. 그게 관련된 유일한 비용입니다.
그렇다면 왜 일반 변수는 비용이 많이 들지 않습니까? T 유형의 변수가 있고 T가 int로 구성되면 int, period 유형의 변수가 있습니다. int 유형의 변수는 저장 위치이며 int를 포함합니다. 스토리지 위치가 스택에 있는지 힙에 있는지 여부는 완전히 관련이 없습니다 . 관련이있는 것은 저장 위치 에 힙에있는 항목에 대한 참조를 포함 하는 대신 int 가 포함 되어 있다는 것입니다 . 저장 위치에 int가 포함되어 있으므로 boxing 및 unboxing 비용을 부담 할 필요가 없습니다. 힙에 새 저장소를 할당하고 int를 새 저장소에 복사하는 것입니다.
이제 분명합니까?
Generics를 사용하면 목록의 내부 배열이 boxing이 필요한 int[]
효과적인 대신 입력 될 수 있습니다 object[]
.
제네릭 없이는 다음과 같은 일이 발생합니다.
- 전화
Add(1)
하세요. - 정수
1
는 객체에 박스로 묶여서 힙에 새 객체를 생성해야합니다. - 이 개체는에 전달됩니다
ArrayList.Add()
. - 박스형 개체는
object[]
.
여기에는 세 가지 수준의 간접 지정이 있습니다 ArrayList
.-> object[]
-> object
-> int
.
제네릭 사용 :
- 전화
Add(1)
하세요. - int 1이에 전달됩니다
List<int>.Add()
. - int는
int[]
.
따라서 간접적 인 수준은 두 가지뿐입니다 : List<int>
-> int[]
-> int
.
몇 가지 다른 차이점 :
- 제네릭이 아닌 방법은 값을 저장하기 위해 8 또는 12 바이트 (하나의 포인터, 하나의 int)의 합계가 필요합니다. 한 할당에는 4/8, 다른 할당에는 4가 필요합니다. 그리고 이것은 아마도 정렬과 패딩 때문일 것입니다. 일반적인 방법은 배열에 4 바이트의 공간 만 필요합니다.
- 제네릭이 아닌 메서드는 boxed int를 할당해야합니다. 일반적인 방법은 그렇지 않습니다. 이는 더 빠르고 GC 변동을 줄입니다.
- The non-generic method requires casts to extract values. This is not typesafe and it's a bit slower.
An ArrayList only handles the type object
so to use this class requires casting to and from object
. In the case of value types, this casting involves boxing and unboxing.
When you use a generic list the compiler outputs specialized code for that value type so that the actual values are stored in the list rather than a reference to objects that contain the values. Therefore no boxing is required.
The price of boxing (to my understanding) is the need to create an object on the heap, copy the stack allocated integer to the new object and vice-versa for unboxing.
I think you are assuming that value types are always instantiated on the stack. This is not the case - they can be created either on the heap, on the stack or in registers. For more information about this please see Eric Lippert's article: The Truth About Value Types.
In .NET 1, when the Add
method is called:
- Space is allocated on the heap; a new reference is made
- The contents of the
i
variable is copied into the reference - A copy of the reference is put at the end of the list
In .NET 2:
- A copy of the variable
i
is passed to theAdd
method - A copy of that copy is put at the end of the list
Yes, the i
variable is still copied (after all, it's a value type, and value types are always copied - even if they're just method parameters). But there's no redundant copy made on the heap.
Why are you thinking in terms of WHERE
the values\objects are stored? In C# value types can be stored on stack as well as heap depending upon what the CLR chooses.
Where generics make a difference is WHAT
is stored in the collection. In case of ArrayList
the collection contains references to boxed objects where as the List<int>
contains int values themselves.
참고URL : https://stackoverflow.com/questions/4403055/boxing-and-unboxing-with-generics
'IT Share you' 카테고리의 다른 글
메시지 로그 수준에 따라 Python의 로깅 형식을 수정할 수 있습니까? (0) | 2020.11.23 |
---|---|
MySQL에서 삭제 후 자동 증가 (0) | 2020.11.23 |
Ruby에서 탭으로 구분 된 파일을 구문 분석하는 가장 좋은 방법은 무엇입니까? (0) | 2020.11.23 |
fold와 foldLeft 또는 foldRight의 차이점은 무엇입니까? (0) | 2020.11.23 |
배열을 C의 함수에 인수로 전달 (0) | 2020.11.23 |