Like @layer8 said, pass by copy and pass by value are the same.
C# copies C++ behavior where you can pass a struct by value or reference, and you can mark the parameter as readonly. C# also has in/out parameters. Essentially, you can program in C# exactly like you would in C++.
The footgun with C# structs are that you can accidentally box them onto the heap. To avoid that you can define `ref struct`s that cannot be boxed. `ref struct`s follow the C# disposable pattern.
I don’t see a difference between pass by copy and pass by value.
The mutability difference is that part of a struct can be modified in place, which value classes can’t: the value of a complete value-class variable (or array slot) can only be modified (reassigned) as a whole. This is presumably because object references to value-class objects can be created, and those objects should be immutable so their identity doesn’t matter.
My point is that pass by copy and pass by value do the same thing, they copy the value representation. In other words, pass by copy means exactly pass by value.
Actually, Java only has pass-by-value, even for reference types. (The same way as C does).
People really misuse/misunderstand this term: Java objects are passed by their pointers ("references") being copied.
The alternative is pass by reference, which is done by e.g. c++, rust, who actually have references (Java doesn't). A good litmus test is whether you can write a swap method that actually changes your local variables.
I think that's mostly a semantic difference - Java avoided the problem of strange lifetimes, captures, tearing by fixing the semantics as immutable value objects, while C# has to deal with these issues.
But under the hood it can (and will) do a modification in place.
For me, a struct in C/C# can be modified and is passed by copy while a value class can not be modified and is passed by value.
I do not think you can do stack allocation in Java.